2011-03-18 22 views
6

Yeni eş zamanlı koleksiyonları okudum ve özellikle ConcurrentBag dikkatimi çekti. ConcurrentBag, öğeleri takip etmek için her bir iş parçacığı üzerinde yerel bir kümeyi dahili olarak tuttuğundan, bu, iş parçacığının kendisi kapsam dışına çıktığında, ConcurrentBag tarafından bellekte referans alınacağı anlamına gelir. Bu da, iş parçacığı tarafından talep edilen bellek, hem de yerel kaynaklar anlamına gelir? (.NET iş parçacığının tam iç işleyişini bilmediğim için özür dilerim)ConcurrentBag içinde olası ezbere?

Birden çok iş parçacığına sahip bir web hizmetinde, görevler ekleyen çok sayıda istemci hizmeti için 1 global ConcurrentBack'inizin bulunduğu bir kullanıcı hesabı alabiliyorum. Bu görevler iş parçacığı üzerinde iş parçacıkları tarafından eklenir. Artık iş parçacığı, iş parçacıklarını yönetmek için çok etkili bir yoldur, ancak iş miktarına bağlı olarak iş parçacıklarını kaldırır ve oluşturur. Bu nedenle, böyle bir web servisi, zaman zaman, altta yatan torbanın hala tahrip olmuş bir çok dişe atıfta bulunmasından ötürü zaman zaman kendini sorunlu bulabilir.

Ben bu davranışı test etmek için hızlı bir uygulamayı yarattı:

static ConcurrentBag<int> bag = new ConcurrentBag<int>(); 
    static void FillBag() { for (int i = 0; i < 100; i++) { bag.Add(i); } } 
    static void PrintState() { Console.WriteLine("Bag size is: {0}", bag.Count); } 
    static void Main(string[] args) 
    { 
     var remote = new Thread(x => 
     { 
      FillBag(); 
      PrintState(); 
     }); 
     // empty bag 
     PrintState(); 
     // first 100 items are added on main thread 
     FillBag(); 
     PrintState(); 
     // second 100 items are added on remote thread 
     remote.Start(); 
     remote.Join(); 
     // since the remote thread is gone out of scope, what happened to its local storage which is part of the bag? 
     PrintState(); 
     // now force a cleanup 
     WeakReference weakRemoteReference = new WeakReference(remote); 
     remote = null; 
     GC.Collect(); 
     GC.WaitForPendingFinalizers(); 
     // Now check if the thread still exists 
     if (weakRemoteReference.IsAlive) 
      Console.WriteLine("Remote thread still exists"); 
     PrintState(); 
     Console.ReadLine(); 

Ve çıkış öykümü doğruluyor:

Bag size is: 0 
Bag size is: 100 
Bag size is: 200 
Bag size is: 200 
Remote thread still exists 
Bag size is: 200 

beklenen bu davranış mı benim testinde bir hata yaptın ya da bu tasarım hatası olarak düşünülebilir mi?

+0

Uzaktaki iş parçacığının, kapsam dışında kaldığını varsayarım, bunun hala kapsamda olduğunu ve şunu söylemelidir: "Uzak iş parçacığı bittiğinden" – Polity

cevap

8

ConcurrentBagConcurrentBag gerçekten de iş parçacığı yerel depolama biriminde tutulur ve iş parçacıklarını bırakırsanız bellek sızıntısına neden olabilir. Bununla birlikte, uygulama başka bir iş parçacığına vermek için bir iş parçacığı listesinden öğeleri "çalabilir". Eğer o programı çalıştırırsanız

ConcurrentBag<int> MyBag = new ConcurrentBag<int>(); 

void DoIt() 
{ 
    for (int i = 0; i < 10; ++i) 
    { 
     MyBag.Add(i); 
    } 

    ThreadPool.QueueUserWorkItem(EmptyBag); 

    Console.Write("Press Enter:"); 
    Console.ReadLine(); 

    Console.WriteLine("{0} items in bag", MyBag.Count); 
} 

void EmptyBag(object state) 
{ 
    int take; 
    while (MyBag.TryTake(out take)) 
    { 
     Console.WriteLine(take); 
    } 
    Console.WriteLine("Bag is empty"); 
} 

ve Giriş tuşuna basınız önce "Çanta boş" mesajı kadar bekleyin siz çanta gerçekten boşaltılmış olduğunu göreceksiniz,: Aşağıdaki yazarsam eylem görebilirsiniz.

Bu yüzden, çantadan bir iplik okunduğu sürece, en sonunda boşaltılacaktır. Tüm öğeler diğer iş parçacıkları tarafından eklenmiş olsa bile.

Evet, olası bir bellek sızıntısı var. Pratikte, eğer birden çok iplik torbasına erişiyorsa, muhtemelen bir endişe değildir.

+0

İki iş parçacığının burada olduğunu tahmin ediyorum ve QueueUserWorkItem yeni bir iş parçacığı oluşturur . Sızan (kaynak veya çalınan kopya) hangisi? Bu işlemi nasıl durdurabilirim ve nasıl temizleyebilirim? Bir görevde iptal etme belirteci kullanıyorum ... – LamonteCristo

+0

@ makerofthings7: Bu örnek kodda, ana iş parçacığı torbasına sahiptir ve 'QueueUserWorkItem 'tarafından oluşturulan iş parçacığı torbayı boşaltır. Bu örnekte hiçbir şey sızmıyor. Genelde, çantaya bir şeyler eklerseniz ve onları çıkarmazsanız bir sızıntı olacaktır. Daha fazla bilgi istiyorsanız bir soru göndermeyi düşünebilirsiniz. –