In-Process Sunucu İş Parçacığı Yönetimi Sorunları

Warning

.NET kodundan apartmanlar arası marshaling: Bir .NET uygulaması, ThreadingModel çağıran apartmanla eşleşmeyen işlem içi bir COM nesnesi oluşturduğunda, COM nesneyi farklı bir apartmanda oluşturur ve bir proxy döndürür. Bu ara sunucu aracılığıyla yöntemleri çağırmak, hazırlama ek yüküne neden olur ve hedef dairenin iş parçacığı engellenirse kilitlenmelere neden olabilir.

Yaygın hata deseni:

// A .NET console app defaults to MTA (no explicit CoInitializeEx).
// Creating an Apartment-threaded COM object forces COM to spin up
// a hidden STA thread. If that STA thread has no message pump,
// calls that require marshaling back to it will hang.

// Fix: If your COM object requires STA, mark Main with [STAThread]:
[STAThread]
static void Main(string[] args)
{
    var comObj = new MyCOMObject(); // Created in the STA — no proxy needed
}

İşlem içi COM sunucularının .NET tüketicileri için en iyi yöntemler:

  • Nesnenin, HKCR\CLSID\{...}\InprocServer32 altındaki ThreadingModel kayıt defteri değerini denetleyin.
  • Apartment iş parçacığı modeli kullanan nesneler kullanıyorsanız giriş noktanızda [STAThread] kullanın (WinForms/WPF bunu otomatik olarak yapar)
  • Bir STA iş parçacığında Thread.Join() veya Task.Wait() asla çağırmayın — await kullanın ya da Dispatcher.PushFrame() iletilerini pompalayın

İşlem içi sunucu, CoInitialize, CoInitializeExveya OleInitialize'ı çağırarak iş parçacığı modelini işaretlemez. İş parçacığı kullanan DLL tabanlı veya işlem içi nesneler için, iş parçacığı modelini kayıt defterinde ayarlamanız gerekir. İş parçacığı modeli belirtmediğinizde varsayılan model işlem başına tek iş parçacığıdır. Modeli belirtmek için ThreadingModel değerini kayıt defterindeki InprocServer32 anahtarına eklersiniz.

Sınıf nesnesinin örneğini oluşturmayı destekleyen DLL'ler DllGetClassObjectve DllCanUnloadNowişlevleri uygulamalı ve dışarı aktarmalıdır. İstemci DLL'nin desteklediği sınıfın bir örneğini istediğinde, CoGetClassObject çağrısı (doğrudan veya CoCreateInstanceçağrısı aracılığıyla), nesne DLL'de uygulandığında sınıf nesnesine bir işaretçi almak için DllGetClassObject çağırır. DllGetClassObject bu nedenle birden çok sınıf nesnesi veya tek bir iş parçacığı güvenli nesne verebilmelidir (temelde iç başvuru sayılarında InterlockedIncrement/InterlockedDecrement kullanarak).

Adından da anlaşılacağı gibi, DllCanUnloadNow, onu uygulayan DLL'nin kullanımda olup olmadığını belirlemek için çağrılır ve eğer kullanımda değilse, çağıran tarafından güvenli bir şekilde kaldırılmasını sağlar. Herhangi bir iş parçacığından CoFreeUnusedLibraries çağrıları, her zaman DllCanUnloadNowçağrısını yapmak üzere ana apartmanın iş parçacığı üzerinden yönlendirilir.

Diğer sunucular gibi işlem içi sunucular da tek iş parçacıklı, daire iş parçacıklı veya serbest iş parçacıklı olabilir. Bu sunucular, bu istemci tarafından kullanılan iş parçacığı modelinden bağımsız olarak herhangi bir OLE istemcisi tarafından kullanılabilir.

İstemciler ve işlem içindeki nesneler arasında iş parçacığı modeli birlikte çalışabilirliğinin tüm kombinasyonlarına izin verilir. İstemci ile farklı iş parçacığı oluşturma modelleri kullanan bir işlem içi nesne arasındaki etkileşim, istemciler ve işlem dışı sunucular arasındaki etkileşime tam olarak benzer. İşlem içi bir sunucu için, istemcinin ve işlem içi sunucunun iş parçacığı modeli farklı olduğunda, COM'un kendisini istemciyle nesnesi arasında bir araya alması gerekir.

Tek iş parçacıklı modeli destekleyen bir işlem içi nesne bir istemcinin birden çok iş parçacığı tarafından aynı anda çağrıldığında, COM istemci iş parçacıklarının nesnenin arabirimine doğrudan erişmesine izin veremez (nesne bu tür bir erişim için tasarlanmamıştır). Bunun yerine, COM çağrıların eşitlenmiş olduğundan ve yalnızca nesneyi oluşturan istemci iş parçacığı tarafından yapıldığından emin olmalıdır. Bu nedenle COM, nesneyi istemcinin ana dairesinde oluşturur ve diğer tüm istemci dairelerinin ara sunucuları kullanarak nesneye erişmesini gerektirir.

İstemciye bağlı bir serbest iş parçacıklı daire (çok iş parçacıklı daire modeli) bir apartman iş parçacıklı işlem içi sunucu oluşturduğunda, COM istemcide tek iş parçacıklı bir apartman modeli "konak" iş parçacığını başlatır. Bu ana iş parçacığı, nesneyi oluşturur ve arabirim işaretçisi istemcinin serbest iş parçacıklı birimine aktarılır. Benzer şekilde, bir apartman modeli istemcisindeki tek iş parçacıklı bir daire modeli serbest iş parçacıklı bir işlem içi sunucu oluşturduğunda, COM serbest iş parçacıklı bir konak iş parçacığı (nesnenin oluşturulup ardından istemci tek iş parçacıklı daire modeline geri yönlendirileceği çok iş parçacıklı apartman) oluşturur.

Not

Genel olarak, işlem içi bir sunucuda özel bir arabirim tasarlarsanız, COM'un istemci birimleri arasında arabirimi taşırması için taşıma kodunu da sağlamanız gerekir.

 

COM, tek iş parçacıklı DLL tarafından sağlanan nesnelere, oluşturuldukları istemci daireden erişim gerektirerek erişimin korunmasına yardımcı olur. Buna ek olarak, tüm DLL giriş noktalarına (dllgetclassobjectve DllCanUnloadNowgibi) ve genel verilere her zaman aynı daire tarafından erişilmelidir. COM, istemcinin ana dairesinde bu tür nesneler oluşturur ve ana daireye nesnenin işaretçilerine doğrudan erişim verir. Diğer dairelerden gelen çağrılar, proxy'den ana dairedeki saplamaya ve ardından nesneye gitmek için iş parçacıkları arası marshaling kullanır. Bu, COM'un nesneye çağrıları eşitlemesine olanak tanır. İş parçacığı arası çağrılar yavaş olduğundan, bu sunucuların birden çok daire modelini destekleyecek şekilde yeniden yazılması önerilir.

Tek iş parçacıklı bir işlem içi sunucu gibi, bir apartman modeli DLL'sinin sağladığı nesneye, oluşturulduğu istemci dairesi tarafından erişilmelidir. Ancak, bu sunucu tarafından sağlanan nesneler istemcinin birden çok apartmanında oluşturulabilir, bu nedenle sunucunun çoklu iş parçacıklı kullanım için giriş noktalarını (DllGetClassObject ve DllCanUnloadNow) uygulaması gerekir. Örneğin, bir istemcinin iki dairesi aynı anda işlem içi nesnenin iki örneğini oluşturmaya çalışırsa, DllGetClassObject her iki daire de aynı anda çağrılabilir. DllCanUnloadNow, kod DLL'de yürütülürken DLL'nin yüklenmemesi için yazılmalıdır.

DLL, tüm nesneleri oluşturmak için yalnızca bir sınıf fabrika işlevi örneği sağlıyorsa, birden çok istemci alanı tarafından erişileceği için sınıf fabrika işlevi uygulaması da çok iş parçacıklı kullanım için tasarlanmalıdır. DllGetClassObject her çağrıldığında DLL, sınıf fabrikasının yeni bir örneğini oluşturursa, sınıf fabrikasının iş parçacığı güvenli olması gerekmez.

Sınıf fabrikası tarafından oluşturulan nesnelerin iş parçacığı güvenliğine sahip olması gerekmez. bir iş parçacığı tarafından oluşturulduktan sonra, nesneye her zaman bu iş parçacığı üzerinden erişilir ve nesneye yapılan tüm çağrılar COM tarafından eşitlenir. Bu nesneyi oluşturan istemcinin apartman modeli dairesi, nesneye doğrudan bir işaretçi alır. Nesnenin oluşturulduğu daireden farklı olan istemci daireler proxy'ler aracılığıyla nesneye erişmelidir. Bu proxy'ler, istemci daireleri arasındaki arabirimi sıraladığında oluşturulur.

İşlem içi DLL ThreadingModel değeri "Both" olarak ayarlandığında, bu DLL tarafından sağlanan bir nesne, tek iş parçacıklı veya çok iş parçacıklı istemci çalışma alanlarında doğrudan (proxy olmadan) oluşturulabilir ve kullanılabilir. Ancak, doğrudan yalnızca oluşturulduğu daire içinde kullanılabilir. Nesneyi başka bir daireye vermek için, nesnenin hazırlanması gerekir. DLL nesnesi, kendi senkronizasyonunu uygulamalıdır ve aynı anda birden çok istemci iş parçacığı tarafından erişilebilir.

COM, işlem içi DLL nesnelerine serbest iş parçacıklı erişimin performansını hızlandırmak için CoCreateFreeThreadedMarshaler işlevini sağlar. Bu işlev, işlem içi sunucu nesnesiyle bir arada kullanılabilir bağımsız iş parçacıklı bir yönlendirme nesnesi oluşturur. Aynı işlemdeki bir istemci bölümünün başka bir bölümdeki bir nesneye erişmesi gerektiğinde, serbest iplik dizicisinin toplanması, istemcinin nesnenin arabirimini farklı bir bölüme aktarırken, istemciye bir proxy yerine sunucu nesnesine doğrudan bir işaretçi sağlar. İstemcinin herhangi bir eşitleme yapması gerekmez. Bu yalnızca aynı süreç içinde çalışır; başka bir sürece gönderilen nesneye bir referans için standart marşal etme kullanılır.

Important

CoCreateFreeThreadedMarshaler için modern alternatif: Windows 8.1+ hedefleyen yeni kodlarda RoGetAgileReference ve IAgileReference arabirimini tercih edin. Bunlar, free-threaded marshaler’ın yol açtığı sakıncalara düşmeden, daireler arasında nesne başvurularını aktarmanın daha güvenli bir yolunu sunar (çünkü bu, daire korumasını tamamen devre dışı bırakır ve iş parçacığı hatalarını gizleyebilir). WinRT nesneleri için çeviklik varsayılan davranıştır; WinRT nesneleri, açıkça devre dışı bırakmadıkları sürece IAgileObject öğesini otomatik olarak destekler.

Yalnızca serbest iş parçacığını destekleyen bir işlem içi DLL tarafından sağlanan nesne, serbest iş parçacıklı bir nesnedir. Kendi senkronizasyonunu uygular ve aynı anda birden fazla istemci iş parçacığı tarafından erişilebilir. Bu sunucu, iş parçacıkları arasındaki arabirimleri hazırlamaz, bu nedenle bu sunucu doğrudan (proxy olmadan) yalnızca istemcideki çok iş parçacıklı daireler tarafından oluşturulabilir ve kullanılabilir. Bunu oluşturan tek iş parçacıklı daireler buna bir ara sunucu aracılığıyla erişecektir.

Daireler arasında Arabirimlere Erişim

İş Parçacığı Modeli Seçimi

Çok İplikli Uygulamalar

İşlemler, İş Parçacıkları ve Apartmanlar

Single-Threaded ve Çoklu İş Parçacığı İletişimi

Single-Threaded Daireler