2015-03-10 19 views
9

HashSet güvenli iplik .NET kaynak kodunda HashSet<T> sınıfında Contains için kod baktığımızda İçeriyor?İş, ben <code>Contains</code> parçacığı güvenli değil bir neden bulamıyorum <T>

HashSet<T> değerini önceden yükleyerek yükledikten sonra çok iş parçacıklı olarak Contains denetleniyor. AsParallel() döngü.

Bunun güvenli olmayacağının bir nedeni var mı? ConcurrentDictionary'u gerçekten değer depolamadığı zaman kullanıyorum.

+0

Bunu bir kez yazıyorsunuz ve daha sonra birden çok iş parçacığından mı okuyorsunuz? –

+4

İçerikleri, kümeden herhangi bir şey eklemediğinizden/çıkardığınız sürece (kullanmıyorken) – nafas

+2

numaralı kılavuzda güvenlidir. Neden kılavuzu okumadınız? https://msdn.microsoft.com/en-us/library/bb359438.aspx: * Bu türdeki herkese açık statik (Visual Basic'te Paylaşılan) üyeler iş parçacığı için güvenlidir. Herhangi bir örnek üyenin iş parçacığı için güvenli olduğu garanti edilmez. * –

cevap

10

Normalde (normalde) sadece okuma için kullanılan koleksiyonları "gayri resmi" güvenli iplik (O okuma sırasında kendisini değiştirir biliyorum .NET hiçbir koleksiyon yoktur). Bazı uyarılar vardır: kendilerini iplik olamazdı

  • ürün güvenli (ondan öğeleri ayıklamak olamaz çünkü ancak HashSet<T> ile bu sorun, en aza indirilmelidir Hala GetHashCode() ve Equals() parçacığı olmalıdır. Örneğin, isteğe bağlı olarak yüklenen tembel nesneler kullanıyorlarsa, iş parçacığı güvenli olmayabilir veya belki sonraki verileri hızlandırmak için bazı verileri önbelleğe alabilir/belleğe atabilirler)
  • Son yazım Thread.MemoryBarrier() (yazma ile aynı iş parçacığında yapılır) veya eşdeğerdir, aksi halde başka bir iş parçacığındaki bir okuma eksik verileri okuyabilir
  • Her bir iş parçacığında (bir yazı yazdığınızdan farklı olarak), ilk okumayı yapmadan önce bir Thread.MemoryBarrier() olduğundan emin olmalısınız. HashSet<T>, diğer konuları oluşturmadan/başlatmadan önce (hazırda Thread.MemoryBarrier() ile) hazırlandıysa, Thread.MemoryBarrier()'un gerekli olmadığını unutmayın, çünkü iş parçacıklarının bellekte eski bir okuması olamaz (çünkü yoktu). Çeşitli işlemler bir örtük Thread.MemoryBarrier() neden olur. HashSet<T> dolu (artı Thread.MemoryBarrier()) sonra örneğin HashSet<T> doldurulmadan önce oluşturulmuş iplikleri, bir Wait() bir sınıfın bir kapalı Thread.MemoryBarrier()

Basit bir örnek yol çıkan, un-Waited bir Wait() girdi ve eğer bu Memoization/tembel yükleme/onu kullanmak istediğinizi kullanır ve bu şekilde iplik güvenliğini bozabilir.

public class MyClass 
{ 
    private long value2; 

    public int Value1 { get; set; } 

    // Value2 is lazily loaded in a very primitive 
    // way (note that Lazy<T> *can* be used thread-safely!) 
    public long Value2 
    { 
     get 
     { 
      if (value2 == 0) 
      { 
       // value2 is a long. If the .NET is running at 32 bits, 
       // the assignment of a long (64 bits) isn't atomic :) 
       value2 = LoadFromServer(); 

       // If thread1 checks and see value2 == 0 and loads it, 
       // and then begin writing value2 = (value), but after 
       // writing the first 32 bits of value2 we have that 
       // thread2 reads value2, then thread2 will read an 
       // "incomplete" data. If this "incomplete" data is == 0 
       // then a second LoadFromServer() will be done. If the 
       // operation was repeatable then there won't be any 
       // problem (other than time wasted). But if the 
       // operation isn't repeatable, or if the incomplete 
       // data that is read is != 0, then there will be a 
       // problem (for example an exception if the operation 
       // wasn't repeatable, or different data if the operation 
       // wasn't deterministic, or incomplete data if the read 
       // was != 0) 
      } 

      return value2; 
     } 
    } 

    private long LoadFromServer() 
    { 
     // This is a slow operation that justifies a lazy property 
     return 1; 
    } 

    public override int GetHashCode() 
    { 
     // The GetHashCode doesn't use Value2, because it 
     // wants to be fast 
     return Value1; 
    } 

    public override bool Equals(object obj) 
    { 
     MyClass obj2 = obj as MyClass; 

     if (obj2 == null) 
     { 
      return false; 
     } 

     // The equality operator uses Value2, because it 
     // wants to be correct. 
     // Note that probably the HashSet<T> doesn't need to 
     // use the Equals method on Add, if there are no 
     // other objects with the same GetHashCode 
     // (and surely, if the HashSet is empty and you Add a 
     // single object, that object won't be compared with 
     // anything, because there isn't anything to compare 
     // it with! :-)) 

     // Clearly the Equals is used by the Contains method 
     // of the HashSet 
     return Value1 == obj2.Value1 && Value2 == obj2.Value2; 
    } 
} 
+0

+1 ben "Gayri resmi" biraz daha fazla vurgulamak. Örneğin, bu özel durumda AFAICT örneğinde olmadığı gibi, işlemlerin hapsetme nedeniyle iş parçacığı için güvenli olması mümkün değildir. –

+0

@JonHanna Ekledim * Hala GetHashCode() ve Equals() iş parçacığı güvenli olmalıdır. Örneğin, isteğe bağlı olarak yüklenen tembel nesnelere erişirlerse, iş parçacığı güvenli olmayabilir ya da sonraki işlemleri hızlandırmak için veri önbelleği olabilirler * – xanatos

+0

Evet, genelleştirmeyi daha çok düşünüyorum; Burada söylediğiniz hiçbir şey uzaktan yanlıştır, ama bunu okuyan birini ve "normal" kelimesini geçmeyi tamamıyla görebiliyorum. Birisinin, cevabınızın geçerli olduğundan emin olmak için söz konusu kodu incelememiz gerekiyor. –