C # çeşitli iş parçacığı eşitleme seçenekleri arasındaki farklar nelerdir?


Yanıtlar:


135

Harika bir soru. Belki yanılıyorum .. Deneyeyim .. Orig cevabımın 2. revizyonu .. biraz daha anlayışla. Beni okuduğunuz için teşekkürler :)

Kilit (obj)

  • (nesne içi?) iplik senkronizasyonu için bir CLR yapısıdır. Yalnızca bir iş parçacığının nesnenin kilidinin sahipliğini alabilmesini ve kilitli kod bloğunu girmesini sağlar. Diğer iş parçacıkları, mevcut sahibin kod bloğundan çıkarak kilidi bırakmasını beklemelidir. Ayrıca, sınıfınızın özel bir üye nesnesini kilitlemeniz önerilir.

Monitörler

  • lock (obj) dahili olarak bir Monitör kullanılarak uygulanır. Kilitleme (obj) 'i tercih etmelisiniz çünkü temizleme prosedürünü unutmak gibi bir şey yapmanıza engel olur. Eğer salakça, Monitör yapısına sahip olacaksınız.
    Monitör kullanımı genellikle mutekslere göre tercih edilir, çünkü monitörler özellikle .NET Framework için tasarlanmıştır ve bu nedenle kaynakları daha iyi kullanır.

Bir kilit veya monitör kullanmak, iş parçacığına duyarlı kod bloklarının aynı anda yürütülmesini önlemek için yararlıdır, ancak bu yapılar bir iş parçacığının bir olayı diğerine iletmesine izin vermez. Bu, iş parçacıklarını etkinleştirmek ve askıya almak için kullanılabilen, iki durumdan birine sahip, sinyalli ve sinyalsiz nesneler olan senkronizasyon olaylarını gerektirir . Muteks, Semaforlar OS düzeyinde kavramlardır. örneğin adlandırılmış bir muteks ile birden çok (yönetilen) exe arasında senkronize edebilirsiniz (uygulamanızın yalnızca bir örneğinin makinede çalışmasını sağlar).

Karşılıklı dışlama:

  • Ancak monitörlerden farklı olarak, iş parçacıkları süreçler arasında eşitlemek için bir muteks kullanılabilir. Süreçler arası senkronizasyon için kullanıldığında, bir mutex bir denir adlı mutex başka uygulamada kullanılacak olması nedeniyle, ve bu nedenle küresel veya statik değişkeni vasıtasıyla paylaşılamaz. Her iki uygulamanın da aynı muteks nesnesine erişebilmesi için bir ad verilmelidir. Buna karşılık, Mutex sınıfı bir Win32 yapısına sarıcıdır. Bir monitörden daha güçlü olmasına rağmen, muteks, Monitor sınıfının gerektirdiğinden daha hesaplama açısından pahalı olan birlikte çalışma geçişleri gerektirir.

Semaforlar (beynime zarar verdi).

  • Bir kaynak havuzuna erişimi denetlemek için Semaphore sınıfını kullanın. İş parçacıkları, semaforu WaitHandle sınıfından devralınan WaitOne yöntemini çağırarak girer ve Release yöntemini çağırarak semaforu serbest bırakır. Bir semafordaki sayım, bir iplik semafora her girdiğinde azaltılır ve bir iplik semaforu serbest bıraktığında artar. Sayı sıfır olduğunda, sonraki istekler diğer dişler semaforu serbest bırakana kadar engellenir. Tüm evreler semaforu serbest bıraktığında, sayı semafor oluşturulduğunda belirtilen maksimum değerdedir. Bir iş parçacığı semafor birden çok kez girebilirsiniz .. Semafor sınıfı WaitOne veya Release .. iş parçacığı kimliğini zorlamıyor. Semaforlar iki türdür: yerel semaforlar ve adlandırılmışsistem semaforları. Bir adı kabul eden bir yapıcı kullanarak bir Semaphore nesnesi oluşturursanız, bu ad, o adın işletim sistemi semaforuyla ilişkilendirilir. Adlandırılmış sistem semaforları işletim sistemi genelinde görünür ve işlemlerin etkinliklerini senkronize etmek için kullanılabilir. Yerel bir semafor yalnızca işleminizde bulunur. İşleminizdeki yerel Semaphore nesnesine başvurusu olan herhangi bir iş parçacığı tarafından kullanılabilir. Her Semafor nesnesi ayrı bir yerel semafordur.

OKUMA SAYFASI - Konu Senkronizasyonu (C #)


18
Monitorİletişime izin vermediğini iddia ediyorsunuz ; Hala Pulsevb. yapabilirsinizMonitor
Marc Gravell

3
Semaphores - stackoverflow.com/a/40473/968003 alternatif bir açıklamasına göz atın . Semaforları bir gece kulübünde fedailer olarak düşünün. Kulüpte bir kerede izin verilen özel sayıda insan var. Kulüp doluysa kimsenin girmesine izin verilmez, ancak bir kişi ayrılır ayrılmaz başka biri girebilir.
Alex Klaus

31

Yeniden "Diğer .Net senkronizasyon sınıflarını kullanma" - bilmeniz gereken diğerlerinden bazıları:

CCR / TPL'de ( Paralel Uzantılar CTP) daha fazla (düşük havai) kilitleme yapısı var - ancak IIRC, bunlar .NET 4.0'da kullanılabilir olacak


Basit bir sinyal iletişimi (async op tamamlanması demek) istiyorsanız Yani - Monitor.Pulse gerekir? veya SemaphoreSlim veya TaskCompletionSource mu kullanıyorsunuz?
Vivek

Zaman uyumsuz işlem için TaskCompletionSource kullanın. Temel olarak, konuları düşünmeyi bırakın ve görevler (iş birimleri) hakkında düşünmeye başlayın. Konular bir uygulama detayıdır ve alakalı değildir. Bir TCS döndürerek, sonuçları, hataları döndürebilir veya iptal işlemlerini gerçekleştirebilirsiniz ve diğer eşzamansız işlemlerle (eşzamansız bekleme veya ContinueWith gibi) kolayca birleştirilebilir.
Simon Gillbee

14

ECMA'da belirtildiği gibi, Yansıyan yöntemlerden de görebileceğiniz gibi, kilit ifadesi temel olarak

object obj = x;
System.Threading.Monitor.Enter(obj);
try {
   
}
finally {
   System.Threading.Monitor.Exit(obj);
}

Yukarıda belirtilen örnekte, Monitörlerin nesneleri kilitleyebildiğini görüyoruz.

Onlar gibi işlemler arası eşitleme gerektiğinde Mutexe en yararlıdır edebilir bir dize tanımlayıcı kilidi. Aynı dize tanımlayıcısı, kilidi elde etmek için farklı işlemler tarafından kullanılabilir.

Semaforlar steroidler üzerindeki Muteksler gibidir, maksimum sayıda eşzamanlı erişim sağlayarak eşzamanlı erişime izin verir '. Sınıra ulaşıldığında, semafor, arayanlardan biri semaforu serbest bırakana kadar kaynağa daha fazla erişimi engellemeye başlar.


5
Bu sözdizimsel şeker C # 4'te biraz değiştirildi Check out blogs.msdn.com/ericlippert/archive/2009/03/06/…
Peter Gfader

14

DotGNU'da diş açma için dersleri ve CLR desteği yaptım ve birkaç düşüncem var ...

Çapraz işlem kilitleri gerektirmedikçe Mutex & Semaphores kullanmaktan daima kaçınmalısınız. Bu .NET sınıfları Win32 Mutex ve Semaphores etrafındaki sarmalayıcılardır ve oldukça ağırdırlar (özellikle kilidiniz çekişme altında değilse, çekirdeğe pahalı bir bağlam anahtarı gerektirirler).

Diğerleri de belirtildiği gibi, C # lock deyimi Monitor.Enter ve Monitor.Exit için derleyici büyüsüdür (bir try / son olarak mevcuttur).

Monitörler, Mutexes'in Monitor.Pulse / Monitor.Wait yöntemleriyle sahip olmadığı basit ama güçlü bir sinyal / bekleme mekanizmasına sahiptir. Win32 eşdeğeri, aslında .NET'te WaitHandles olarak da bulunan CreateEvent aracılığıyla olay nesneleri olacaktır. Darbe / Bekleme modeli, Unix'in pthread_signal ve pthread_wait modellerine benzer, ancak daha hızlıdır, çünkü daha önce tartışılmamış durumda tamamen kullanıcı modu işlemleri olabilirler.

Monitor.Pulse / Wait kullanımı kolaydır. Bir iş parçacığında, bir nesneyi kilitleriz, bir bayrağı / durumu / özelliği kontrol ederiz ve beklediğimiz şey bu değilse, Monitor.Wit'i arayın, kilidi serbest bırakır ve bir nabız gönderilinceye kadar bekleyin. Bekleme geri döndüğünde, geri dönüp bayrağı / durumu / özelliği tekrar kontrol ederiz. Diğer iş parçacığında, flag / state / özelliği değiştirdiğimizde nesneyi kilitleriz ve ardından dinleme iş parçacıklarını uyandırmak için PulseAll'ı çağırırız.

Genellikle sınıflarımızın iş parçacığı için güvenli olmasını isteriz, bu yüzden kodumuza kilitler koyarız. Ancak, genellikle sınıfımızın yalnızca bir iş parçacığı tarafından kullanılması söz konusudur. Bu, kilitlerin kodumuzu gereksiz yere yavaşlattığı anlamına gelir ... CLR'deki akıllı optimizasyonlar performansın iyileştirilmesine yardımcı olabilir.

Microsoft'un kilitlerin uygulanmasından emin değilim ama DotGNU ve Mono'da kilit durum bayrağı her nesnenin başlığında saklanıyor. .NET'teki (ve Java'daki) her nesne bir kilit haline gelebilir, böylece her nesnenin bunu başlıklarında desteklemesi gerekir. DotGNU uygulamasında, kilit olarak kullanılan her nesne için genel bir hashtable kullanmanıza izin veren bir bayrak vardır - bu, her nesne için 4 baytlık bir ek yükü ortadan kaldırma avantajına sahiptir. Bu, bellek için harika değildir (özellikle ağır iş parçacığı olmayan gömülü sistemler için), ancak performansa sahiptir.

Hem Mono hem de DotGNU, kilitleme / bekleme gerçekleştirmek için muteksleri etkili bir şekilde kullanır, ancak bir spinlock stili karşılaştırma ve değiştirme kullanın gerçekten gerekli olmadıkça gerçekten bir sabit kilit gerçekleştirme ihtiyacını ortadan kaldırmak için işlemleri kullanır:

Monitörlerin nasıl uygulanabileceğine ilişkin bir örneği burada görebilirsiniz:

http://cvs.savannah.gnu.org/viewvc/dotgnu-pnet/pnet/engine/lib_monitor.c?revision=1.7&view=markup


9

Dize kimliğiyle tanımladığınız herhangi bir paylaşılan Mutex'i kilitlemek için ek bir uyarı, varsayılan olarak bir "Yerel \" muteksi olacak ve terminal sunucusu ortamındaki oturumlar arasında paylaşılmayacaktır.

Paylaşılan sistem kaynaklarına erişimin düzgün bir şekilde kontrol edilmesini sağlamak için dize tanımlayıcınıza "Global \" önekini ekleyin. Ben bunu fark etmeden önce sadece SİSTEM hesabı altında çalışan bir hizmet ile iletişimi senkronize sorunları bir yığın halinde çalışıyordu.


5

Eğer "lock ()", "Mutex" ve "Monitör" önlemek için çalışır ...

.NET 4'te yeni ad alanı System.Collections.Concurrent'a bakın.
Bazı iş parçacığı için güvenli koleksiyon sınıfları vardır

http://msdn.microsoft.com/en-us/library/system.collections.concurrent.aspx

ConcurrentDictionary kayalar! Artık benim için manuel kilitleme yok!


2
Kilitlemekten kaçının, ancak Monitör'ü kullanın? Neden?
mafu

@mafutrct Çünkü senkronizasyona kendiniz dikkat etmeniz gerekir.
Peter Gfader

Oh, şimdi anladım, bahsi geçen üç fikirden TÜMÜNÜ önlemek istedin. Monitor kullanacaksınız gibi görünüyordu ama lock / Mutex kullanmıyordu.
mafu

System.Collections.Concurrent'i asla kullanmayın. Onlar ana yarış koşulları kaynağı ve aynı zamanda arayanlar iş parçacığı engellemek.
Alexander Danilov

-2

Çoğu durumda olmamalıdır kilitleri (= Monitörler) ya da muteksler / semaforları kullanın. Hepsi mevcut iş parçacığını engeller.

Ve kesinlikle kullanmamalısın System.Collections.Concurrent sınıfları - bunlar yarış koşullarının ana kaynağıdır çünkü birden fazla koleksiyon arasındaki işlemleri desteklemez ve aynı zamanda mevcut iş parçacığını engeller.

Şaşırtıcı bir şekilde .NET'in senkronizasyon için etkili mekanizmaları yoktur.

C # üzerinde GCD'den ( dünya) seri kuyruk uyguladım Objc/Swift- çok hafif, testler ile iş parçacığı havuzu kullanan senkronizasyon aracını engellemiyor.

Çoğu durumda her şeyi senkronize etmenin en iyi yolu - veritabanı erişiminden (merhaba sqlite) iş mantığına.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.