ConcurrentBag <> 'den tek ve belirli bir nesne nasıl kaldırılır?


109

ConcurrentBag<T>.NET 4'teki yeniliklerle , yalnızca TryTake()ve TryPeek()kullanılabilir olduğunda belirli, belirli bir nesneyi ondan nasıl kaldırırsınız ?

Kullandığım düşünüyorum TryTake()ve ben o zaman sadece listesine çıkan nesne geri ekleyerek yok kaldırmak istiyor, ama bir şey eksik olabilir gibi hissediyorum. Bu doğru yol mu?

Yanıtlar:


89

Kısa cevap: Bunu kolay bir şekilde yapamazsınız.

ConcurrentBag, her iş parçacığı için bir iş parçacığı yerel kuyruğu tutar ve yalnızca kendi kuyruğu boşaldığında diğer iş parçacıklarının kuyruklarına bakar. Bir öğeyi kaldırıp geri koyarsanız, bir sonraki çıkardığınız öğe yine aynı öğe olabilir. Öğeleri tekrar tekrar kaldırmanın ve geri koymanın tüm öğeleri yinelemenize izin vereceğinin garantisi yoktur.

Sizin için iki alternatif:

  • Tüm öğeleri çıkarın ve çıkarmak istediğinizi bulana kadar hatırlayın, ardından diğerlerini geri koyun. İki iş parçacığı bunu aynı anda yapmaya çalışırsa sorun yaşayacağınızı unutmayın.
  • ConcurrentDictionary gibi daha uygun bir veri yapısı kullanın .

9
SynchronizedCollection da uygun bir ikame olabilir.
ILIA BROUDNO

2
@ILIABROUDNO - bunu cevap olarak koymalısın! Bu, bir Sözlüğe ihtiyacınız olmadığında kludgey ConcurrentDictionary'den ÇOK ÇOK daha iyidir
Denis

2
Bilginize, SynchronizedCollection .NET Core'da kullanılamaz. Bu yorumun tarihi itibariyle, System.Collections.Concurrent türleri .NET Core tabanlı uygulamalar için geçerli yoldur.
Matthew Snyder

2
NET Core'un hangi sürümünün kullanıldığından emin değilim, ancak .NET Core 2.1 SDK'ya dayalı bir proje üzerinde çalışıyorum ve SynchronizedCollection şu anda Collections.Generic ad alanında kullanılabilir.
Lucas Leblanc

15

Yapamazsın. Bu bir çanta, sipariş edilmedi. Geri koyduğunuzda, sonsuz bir döngü içinde sıkışıp kalacaksınız.

Bir Set istiyorsun. ConcurrentDictionary ile birini taklit edebilirsiniz. Veya kendinizi bir kilitle koruduğunuz bir HashSet.


8
Lütfen genişletin. Temel ConcurrentDictionary'de anahtar olarak ne kullanırsınız?
Denise Skidmore

2
Pekala, anahtarın saklamaya çalıştığınız nesnenin türü olacağını ve ardından değerin bazı türlerden bir koleksiyon olacağını varsayıyorum. Bu HashSet, onun tarif ettiği gibi "a taklit eder" .
Mathias Lykkegaard Lorenzen

5

ConcurrentBag, öğeleri ekleyebileceğiniz ve birçok iş parçacığından numaralandırabileceğiniz ve ardından adından da anlaşılacağı gibi sonunda atabileceğiniz bir Listeyi işlemek için harikadır :)

Mark Byers'ın söylediği gibi , kaldırmak istediğiniz öğeyi içermeyen yeni bir ConcurrentBag'i yeniden oluşturabilirsiniz, ancak bunu bir kilit kullanarak birden fazla iş parçacığı isabetine karşı korumanız gerekir. Bu tek satırlık bir:

myBag = new ConcurrentBag<Entry>(myBag.Except(new[] { removedEntry }));

Bu çalışır ve ConcurrentBag'in tasarlandığı ruhla eşleşir.


9
Bu cevabın yanıltıcı olduğunu düşünüyorum. Açık olmak gerekirse, bu istenen Çıkarma işleminde herhangi bir iplik güvenliği SAĞLAMAZ. Ve etrafına bir kilit koymak, eşzamanlı bir koleksiyon kullanma amacını bozar.
ILIA BROUDNO

1
Katılıyorum. Biraz açıklığa kavuşturmak için ConcurrentBag, tamamlandığında tüm içeriği ile doldurulacak, numaralandırılacak ve atılacak şekilde tasarlanmıştır. Benimki de dahil olmak üzere bir öğeyi kaldırma girişimleri kirli bir hack ile sonuçlanacaktır. En azından bir cevap vermeye çalıştım, ancak en iyisi ConcurrentDictionary gibi daha iyi bir eşzamanlı koleksiyon sınıfı kullanmaktır.
Larry,

4

ConcurrentDictionary istediğiniz şekilde çalışacağı için mark doğrudur . Yine de bir ConcurrentBag kullanmak istiyorsanız , aşağıdakiler size yeterli olmayacak, sizi oraya götürecektir.

var stringToMatch = "test";
var temp = new List<string>();
var x = new ConcurrentBag<string>();
for (int i = 0; i < 10; i++)
{
    x.Add(string.Format("adding{0}", i));
}
string y;
while (!x.IsEmpty)
{
    x.TryTake(out y);
    if(string.Equals(y, stringToMatch, StringComparison.CurrentCultureIgnoreCase))
    {
         break;
    }
    temp.Add(y);
}
foreach (var item in temp)
{
     x.Add(item);
}

3

Bahsettiğiniz gibi TryTake(), tek seçenek bu. Bu aynı zamanda MSDN'deki örnektir . Reflektör de başka hiçbir gizli iç yöntem göstermez.


1
public static void Remove<T>(this ConcurrentBag<T> bag, T item)
{
    while (bag.Count > 0)
    {
        T result;
        bag.TryTake(out result);

        if (result.Equals(item))
        {
            break; 
        }

        bag.Add(result);
    }

}

ConcurrentBagsırasız bir koleksiyondur, ancak kodunuz bunu bekler bag.TryTakeve bag.AddFIFO tarzında çalışır. Kodunuz varsayar bagiçerir item, bu bulana kadar döngüler itemiçinde bag. Yalnızca kod yanıtları önerilmemektedir, çözümünüzü açıklamalısınız.
GDavid

-1

Projelerimde kullandığım uzatma sınıfım budur. Tek bir öğeyi ConcurrentBag'den kaldırabilir ve ayrıca ürün listesini çantadan kaldırabilir

public static class ConcurrentBag
{
    static Object locker = new object();

    public static void Clear<T>(this ConcurrentBag<T> bag)
    {
        bag = new ConcurrentBag<T>();
    }


    public static void Remove<T>(this ConcurrentBag<T> bag, List<T> itemlist)
    {
        try
        {
            lock (locker)
            {
                List<T> removelist = bag.ToList();

                Parallel.ForEach(itemlist, currentitem => {
                    removelist.Remove(currentitem);
                });

                bag = new ConcurrentBag<T>();


                Parallel.ForEach(removelist, currentitem =>
                {
                    bag.Add(currentitem);
                });
            }

        }
        catch (Exception ex)
        {
            Debug.WriteLine(ex.Message);
        }
    }

    public static void Remove<T>(this ConcurrentBag<T> bag, T removeitem)
    {
        try
        {
            lock (locker)
            {
                List<T> removelist = bag.ToList();
                removelist.Remove(removeitem);                

                bag = new ConcurrentBag<T>();

                Parallel.ForEach(removelist, currentitem =>
                {
                    bag.Add(currentitem);
                });
            }

        }
        catch (Exception ex)
        {
            Debug.WriteLine(ex.Message);
        }
    }
}

İşe yarayacağına inanmak zor çünkü yerel değişkende yeni ConcurrentBag oluşturuyorsunuz, ancak emin değilim. herhangi bir test?
shtse8

3
Hayır, çalışmıyor. Nasıl çalışmalı, sadece yeni bir referans oluşturuyorsunuz. Eskisi hala eski nesneye işaret ediyor .... "ref" ile çalışırsanız işe
Thomas Christof

-5
public static ConcurrentBag<String> RemoveItemFromConcurrentBag(ConcurrentBag<String> Array, String Item)
{
    var Temp=new ConcurrentBag<String>();
    Parallel.ForEach(Array, Line => 
    {
       if (Line != Item) Temp.Add(Line);
    });
    return Temp;
}

-13

ne dersin:

bag.Where(x => x == item).Take(1);

İşe yarıyor, ne kadar verimli olduğundan emin değilim ...


Bu, çantadan hiçbir şey çıkarmaz. Aldığınız eşya çantanın içinde kalır.
Keith

3
"bag = new ConcurrentBag (bag.Where (x => x! = item))" olmalıdır
atikot

4
@atikot, bu çizgi beni güldürdü
parek
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.