Not
Bu sayfaya erişim yetkilendirme gerektiriyor. Oturum açmayı veya dizinleri değiştirmeyi deneyebilirsiniz.
Bu sayfaya erişim yetkilendirme gerektiriyor. Dizinleri değiştirmeyi deneyebilirsiniz.
Çoklu iş parçacığı kullanımı dikkatli programlama gerektirir. Çoğu görev için, iş parçacığı havuzundaki iş parçacıkları tarafından yürütülecek istekleri kuyruğa alarak karmaşıklığı azaltabilirsiniz. Bu konu başlığında, birden çok iş parçacığının çalışmasını koordine etme veya engelleyen iş parçacıklarıyla başa çıkma gibi daha zor durumların ele alındığı konular ele alınmıştır.
Uyarı
.NET Framework 4'den başlayarak, Görev Paralel Kitaplığı ve PLINQ, çok iş parçacıklı programlamanın karmaşıklığını ve risklerini azaltan API'ler sağlar. Daha fazla bilgi için bkz. .NET'te Paralel Programlama.
Kilitlenmeler ve yarış koşulları
Çoklu iş parçacığı kullanımı aktarım hızı ve yanıt verme hızıyla ilgili sorunları çözer, ancak bunu yaparken yeni sorunlar getirir: kilitlenmeler ve yarış koşulları.
Kilitlenmeler
İki iş parçacığının her biri, diğerinin zaten kilitlediği bir kaynağı kilitlemeye çalıştığında bir çıkmaz durumu oluşur. Hiçbir iplik daha fazla ilerleme kaydedemez.
Yönetilen iş parçacığı sınıflarının birçok yöntemi, kilitlenmeleri algılamanıza yardımcı olmak için zaman aşımı sağlar. Örneğin, aşağıdaki kod lockObject adlı bir nesnede kilit elde etmeye çalışır. Kilit 300 milisaniye içinde alınmadığında, Monitor.TryEnter geri false döner.
If Monitor.TryEnter(lockObject, 300) Then
Try
' Place code protected by the Monitor here.
Finally
Monitor.Exit(lockObject)
End Try
Else
' Code to execute if the attempt times out.
End If
if (Monitor.TryEnter(lockObject, 300)) {
try {
// Place code protected by the Monitor here.
}
finally {
Monitor.Exit(lockObject);
}
}
else {
// Code to execute if the attempt times out.
}
Yarış koşulları
Yarış durumu, bir programın sonucu, iki veya daha fazla iş parçacığından hangisinin önce belirli bir kod bloğuna ulaştığına bağlı olduğunda oluşan bir hatadır. Programı birçok kez çalıştırmak farklı sonuçlar üretir ve belirli bir çalıştırmanın sonucu tahmin edilemez.
Yarış durumu için basit bir örnek, bir alanı artırmaktır. Bir sınıfın, (C#) veya (Visual Basic) gibi bir kod kullanılarak, sınıfın her örneği oluşturulduğunda artırılan bir özel objCt++; alanı (Visual Basic'te objCt += 1) olduğunu varsayalım. Bu işlem, değeri objCt'den bir yazmaca yüklemeyi, değeri artırmayı ve değeri objCt'e depolamayı gerektirir.
Çok iş parçacıklı bir uygulamada, değeri yükleyip artıran bir iş parçacığı, üç adımın tümünü gerçekleştiren başka bir iş parçacığı tarafından kesintiye uğrayabilir; ilk iş parçacığı yürütmeye devam edip değerini depoladığında, bu süreçte değişen değeri hesaba katmadan, değeri üzerine yazar objCt.
Bu belirli yarış durumu, Interlocked sınıfının yöntemleri Interlocked.Increment kullanılarak kolayca önlenir. Verileri birden çok iş parçacığı arasında eşitlemeye yönelik diğer teknikler hakkında bilgi edinmek için bkz. Multithreading için Verileri Eşitleme.
Yarış koşulları, birden çok iş parçacığının etkinliklerini eşitlediğinizde de oluşabilir. Bir kod satırı yazdığınızda, bir iş parçacığı satırı yürütmeden veya satırı oluşturan tek tek makine talimatlarından herhangi birini yerine getirmeden önce durdurulursa ve başka bir iş parçacığı öne geçerse neler olabileceğini dikkate almanız gerekir.
Statik üyeler ve statik oluşturucular
Sınıf, sınıf oluşturucusu (static C# dilindeki oluşturucu, Shared Sub New Visual Basic dilindeki oluşturucu) çalışmasını tamamlayana kadar başlatılmaz. Başlatılmamış bir türdeki kodun yürütülmesini önlemek için ortak dil çalışma zamanı, sınıf oluşturucusunun çalışması tamamlanana kadar diğer iş parçacıklarından sınıfın üyelerine static (Shared Visual Basic'teki üyeler) yapılan tüm çağrıları engeller.
Örneğin, bir sınıf yapıcı yeni bir iş parçacığı başlatırsa ve iş parçacığı yordamı sınıfın bir static üyesini çağırırsa, sınıf yapıcı tamamlanana kadar yeni iş parçacığı bloke olur.
Bu, oluşturucu tanımlayabilen static türü için geçerlidir.
İşlemci sayısı
Bir sistemde birden çok işlemci veya yalnızca bir işlemci olup olmadığı çok iş parçacıklı mimariyi etkileyebilir. Daha fazla bilgi için bkz. İşlemci Sayısı.
Environment.ProcessorCount Çalışma zamanında kullanılabilen işlemci sayısını belirlemek için özelliğini kullanın.
Genel öneriler
Birden çok iş parçacığı kullanırken aşağıdaki yönergeleri göz önünde bulundurun:
Thread.Abort diğer iş parçacıklarını sonlandırmak için kullanmayın. Başka bir iş parçacığında çağrı
Abortyapmak, iş parçacığının işlenmesinde hangi noktaya ulaştığını bilmeden bu iş parçacığında bir özel durum oluşturmakla benzerdir.Birden çok iş parçacığının etkinliklerini eşitlemek için Thread.Suspend ve Thread.Resume kullanmayın. Mutex, ManualResetEvent, AutoResetEvent ve Monitor kullanın.
Ana programınızdan çalışan iş parçacıklarının yürütülmesini denetlemeyin (örneğin, olayları kullanarak). Bunun yerine, iş kullanılabilir olana kadar çalışan iş parçacıklarının beklemesi, işi yürütmesi ve bittiğinde programınızın diğer bölümlerine bildirmesi gerektiği şekilde programınızı tasarlayın. Eğer iş parçacıklarınız engellenmiyorsa, iş parçacığı havuzu iş parçacıklarını kullanmayı göz önünde bulundurun. Monitor.PulseAll , çalışan iş parçacıklarının engellendiği durumlarda kullanışlıdır.
Türleri kilit nesneleri olarak kullanmayın. Yani, C# veya Visual Basic'te
lock(typeof(X))gibi kodlardan veyaSyncLock(GetType(X))nesneleriyle Monitor.Enter kullanımından kaçının. Belirli bir tür için uygulama etki alanı başına yalnızca bir örnek System.Type vardır. Aldığınız kilit türü genel ise, kendi kodunuz dışında başka kodlar da bunun üzerine kilit alabilir, bu da kilitlenmelere yol açabilir. Ek sorunlar için Güvenilirlik En İyi Uygulamaları'na bakın.Örneğin
lock(this)C# veyaSyncLock(Me)Visual Basic'te örnekleri kilitlerken dikkatli olun. Uygulamanızda, türün dışındaki diğer kodlar nesne üzerinde kilit alırsa, kilitlenmelere yol açabilir.Monitöre giren bir iş parçacığının, iş parçacığı monitördeyken bir istisna oluşsa bile her zaman bu monitörden çıktığından emin olun. C# lock deyimi ve Visual Basic SyncLock deyimi, çağrıldığından emin olmak için bir Monitor.Exit bloğu kullanarak bu davranışı otomatik olarak sağlar. Exit'in çağrılmasını sağlayamıyorsanız tasarımınızı Mutex kullanacak şekilde değiştirmeyi göz önünde bulundurun. Bir thread sona erdiğinde, ona ait olan mutex otomatik olarak serbest bırakılır.
Farklı kaynaklar gerektiren görevler için birden çok iş parçacığı kullanın ve tek bir kaynağa birden çok iş parçacığı atamaktan kaçının. Örneğin, G/Ç içeren herhangi bir görev, G/Ç işlemleri sırasında iş parçacığı engellenip diğer iş parçacıklarının yürütülmesine izin verdiği için kendi iş parçacığına sahip olmanın avantajlarından yararlanır. Kullanıcı girişi, ayrılmış iş parçacığından yararlanan başka bir kaynaktır. Tek işlemcili bir bilgisayarda, yoğun hesaplama içeren bir görev kullanıcı girişiyle ve G/Ç içeren görevlerle birlikte bulunur, ancak birden çok işlem yoğunluklu görev birbiriyle bir arada bulunur.
Basit durum değişiklikleri için Interlocked sınıfının yöntemlerini kullanmayı,
lockSyncLockifadesi (Visual Basic'te) kullanmak yerine göz önünde bulundurun. deyimilockiyi bir genel amaçlı araçtır, ancak Interlocked sınıfı atomik olması gereken güncelleştirmeler için daha iyi performans sağlar. İçeride, çekişme olmadığı durumlarda tek bir kilitleme ön eki çalıştırır. Kod incelemelerinde, aşağıdaki örneklerde gösterilen gibi kodu izleyin. İlk örnekte durum değişkeni artırılır:SyncLock lockObject myField += 1 End SyncLocklock(lockObject) { myField++; }Increment yöntemini
lockdeyimi yerine kullanarak performansı aşağıdaki gibi geliştirebilirsiniz:System.Threading.Interlocked.Increment(myField)System.Threading.Interlocked.Increment(myField);Uyarı
Add yöntemini, 1'den büyük atomik artışlar için kullanın.
İkinci örnekte, başvuru türü değişkeni yalnızca null başvuruysa (
NothingVisual Basic'te) güncelleştirilir.If x Is Nothing Then SyncLock lockObject If x Is Nothing Then x = y End If End SyncLock End Ifif (x == null) { lock (lockObject) { x ??= y; } }Performans, bunun yerine CompareExchange yöntemi kullanılarak aşağıdaki gibi artırılabilir:
System.Threading.Interlocked.CompareExchange(x, y, Nothing)System.Threading.Interlocked.CompareExchange(ref x, y, null);Uyarı
Yöntem CompareExchange<T>(T, T, T) aşırı yüklemesi, başvuru türleri için tür açısından güvenli bir alternatif sağlar.
Sınıf kitaplıkları için öneriler
Çoklu iş parçacığı kullanımı için sınıf kitaplıkları tasarlarken aşağıdaki yönergeleri göz önünde bulundurun:
Mümkünse eşitleme gereksiniminden kaçının. Bu özellikle yoğun kullanılan kodlar için geçerlidir. Örneğin bir algoritma, bir yarış durumunu ortadan kaldırmak yerine tolere etmek için ayarlanabilir. Gereksiz eşitleme performansı düşürür ve kilitlenme ve yarış durumları olasılığını oluşturur.
Statik verileri (
SharedVisual Basic'te) iş parçacığını varsayılan olarak güvenli hale getirin.Örnek veri iş parçacığını varsayılan olarak güvenli hale getirmeyin. İş parçacığı güvenli kod oluşturmak için kilit eklemek performansı azaltır, kilit çekişmesi artırır ve kilitlenmelerin oluşma olasılığını oluşturur. Ortak uygulama modellerinde, aynı anda yalnızca bir iş parçacığı kullanıcı kodunu yürütür ve bu da iş parçacığı güvenliği gereksinimini en aza indirir. Bu nedenle, varsayılan olarak .NET sınıf kitaplıkları iş parçacığı güvenli değildir.
Statik durumu değiştiren statik yöntemler sağlamaktan kaçının. Yaygın sunucu senaryolarında statik durum istekler arasında paylaşılır ve bu da birden çok iş parçacığının bu kodu aynı anda yürütebileceği anlamına gelir. Bu, iş parçacığıyla ilgili hataların ortaya çıkma olasılığını artırır. verileri istekler arasında paylaşılmayan örneklere kapsülleyen bir tasarım deseni kullanmayı göz önünde bulundurun. Ayrıca, statik veriler eşitlenirse, durumu değiştiren statik yöntemler arasındaki çağrılar kilitlenmelere veya yedekli eşitlemeye neden olabilir ve performansı olumsuz etkileyebilir.