วิธีการลบวัตถุเดียวออกจาก ConcurrentBag <>?


109

ด้วยสิ่งใหม่ConcurrentBag<T>ใน. NET 4 คุณจะลบออบเจ็กต์เฉพาะบางอย่างออกจากวัตถุได้อย่างไรเมื่อมีเพียงTryTake()และTryPeek()พร้อมใช้งาน

ฉันกำลังคิดว่าจะใช้TryTake()แล้วเพิ่มวัตถุที่เป็นผลลัพธ์กลับเข้าไปในรายการถ้าฉันไม่ต้องการลบออก แต่ฉันรู้สึกว่าฉันอาจจะขาดอะไรไป นี้เป็นวิธีที่ถูกต้องหรือไม่?

คำตอบ:


89

คำตอบสั้น ๆ : คุณไม่สามารถทำได้ด้วยวิธีง่ายๆ

ConcurrentBag เก็บเธรดโลคัลคิวสำหรับแต่ละเธรดและดูเฉพาะคิวเธรดอื่นเมื่อคิวของตัวเองว่างเปล่า หากคุณนำรายการออกและใส่กลับรายการถัดไปที่คุณนำออกอาจเป็นรายการเดิมอีกครั้ง ไม่มีการรับประกันว่าการนำรายการต่างๆออกซ้ำแล้วซ้ำอีกจะทำให้คุณสามารถทำซ้ำรายการทั้งหมดได้

สองทางเลือกสำหรับคุณ:

  • ลบรายการทั้งหมดและจำไว้จนกว่าคุณจะพบรายการที่คุณต้องการลบจากนั้นใส่รายการอื่น ๆ ในภายหลัง โปรดทราบว่าหากสองเธรดพยายามทำสิ่งนี้พร้อมกันคุณจะมีปัญหา
  • ใช้โครงสร้างข้อมูลที่เหมาะสมมากขึ้นเช่นConcurrentDictionary

9
SynchronizedCollection อาจเป็นสิ่งทดแทนที่เหมาะสม
ILIA BROUDNO

2
@ILIABROUDNO - คุณควรตอบว่า! นั่นดีกว่าพจนานุกรมพร้อมกันที่ไม่จำเป็นเมื่อคุณไม่ต้องการพจนานุกรม
เดนิส

2
FYI, SynchronizedCollection ไม่มีใน. NET Core ณ วันที่แสดงความคิดเห็นนี้ประเภท System.Collections.Concurrent เป็นวิธีปัจจุบันในการใช้งาน. NET Core
Matthew Snyder

2
ฉันไม่แน่ใจว่ากำลังใช้. NET Core เวอร์ชันใด แต่ฉันกำลังทำงานกับโปรเจ็กต์ที่ใช้. NET Core 2.1 SDK และ SynchronizedCollection ที่มีอยู่ใน Collections Generic namespace ตอนนี้
Lucas Leblanc

15

คุณทำไม่ได้ กระเป๋ามันไม่ได้สั่ง เมื่อคุณใส่กลับเข้าไปคุณจะติดอยู่ในวงวนที่ไม่สิ้นสุด

คุณต้องการชุด คุณสามารถเลียนแบบได้ด้วย ConcurrentDictionary หรือ HashSet ที่คุณป้องกันตัวเองด้วยการล็อก


8
กรุณาขยายความ คุณจะใช้อะไรเป็นกุญแจสำคัญใน ConcurrentDictionary
Denise Skidmore

2
ฉันคิดว่าคีย์น่าจะเป็นประเภทของอ็อบเจ็กต์ที่คุณพยายามจัดเก็บจากนั้นค่าจะเป็นคอลเล็กชันบางประเภท นั่นจะเป็นการ "เลียนแบบ" HashSetตามที่เขาอธิบาย
Mathias Lykkegaard Lorenzen

5

ConcurrentBag นั้นยอดเยี่ยมในการจัดการรายการที่คุณสามารถเพิ่มรายการและแจกแจงจากเธรดจำนวนมากจากนั้นจึงทิ้งมันไปตามชื่อที่แนะนำ :)

ตามที่ Mark Byers บอกคุณสามารถสร้าง ConcurrentBag ใหม่ที่ไม่มีรายการที่คุณต้องการลบได้ แต่คุณต้องป้องกันสิ่งนี้จากการเข้าชมหลายเธรดโดยใช้การล็อก นี่คือซับเดียว:

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

สิ่งนี้ใช้งานได้และตรงกับจิตวิญญาณที่ ConcurrentBag ได้รับการออกแบบมาสำหรับ


9
ฉันรู้สึกว่าคำตอบนี้ทำให้เข้าใจผิด เพื่อความชัดเจนสิ่งนี้ไม่ได้ให้ความปลอดภัยใด ๆ ในการดำเนินการลบที่ต้องการ และการวางล็อครอบ ๆ มันก็เอาชนะจุดประสงค์ของการใช้คอลเลกชันพร้อมกัน
ILIA BROUDNO

1
ฉันเห็นด้วย. เพื่อชี้แจงเล็กน้อย ConcurrentBag ได้รับการออกแบบมาเพื่อเติมเต็มแจกแจงและโยนทิ้งไปพร้อมกับเนื้อหาทั้งหมดเมื่อเสร็จสิ้น ความพยายามใด ๆ รวมถึงการทุ่นระเบิดเพื่อลบรายการจะส่งผลให้แฮ็คสกปรก อย่างน้อยฉันก็พยายามให้คำตอบแม้ว่าสิ่งที่ดีที่สุดคือการใช้คลาสการรวบรวมพร้อมกันที่ดีกว่าเช่น ConcurrentDictionary
Larry

4

เครื่องหมายถูกต้องเนื่องจากConcurrentDictionaryจะทำงานในแบบที่คุณต้องการ หากคุณต้องการยังคงใช้ConcurrentBagต่อไปนี้ไม่ได้ผลในใจคุณจะพาคุณไปที่นั่น

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

ตามที่คุณพูดถึงTryTake()เป็นทางเลือกเดียว และนี่ก็เป็นตัวอย่างในMSDN ตัวสะท้อนแสงไม่แสดงวิธีการภายในอื่น ๆ ที่น่าสนใจเช่นกัน


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);
    }

}

ConcurrentBagเป็นคอลเล็กชันที่ไม่มีการเรียงลำดับ แต่รหัสของคุณคาดหวังbag.TryTakeและbag.Addทำงานในลักษณะ FIFO รหัสของคุณสมมติว่าbagมีitemมันลูปจนกว่าจะพบในitem bagรหัสคำตอบเท่านั้นที่ท้อใจคุณควรอธิบายวิธีแก้ปัญหาของคุณ
GDavid

-1

นี่คือคลาสส่วนขยายของฉันที่ฉันใช้ในโปรเจ็กต์ของฉัน สามารถลบรายการเดียวจาก ConcurrentBag และยังสามารถลบรายการสินค้าออกจากกระเป๋า

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);
        }
    }
}

มันยากที่จะเชื่อว่ามันใช้งานได้เพราะคุณกำลังสร้าง ConcurrentBag ใหม่ในตัวแปรท้องถิ่น แต่ฉันไม่แน่ใจ การทดสอบใด ๆ
shtse8

3
ไม่มันใช้ไม่ได้ วิธีการทำงานคุณกำลังสร้างข้อมูลอ้างอิงใหม่เท่านั้น ของเก่ายังคงชี้ไปที่วัตถุเก่า .... มันจะได้ผลถ้าคุณทำงานกับ "ref"
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

เกี่ยวกับ:

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

ได้ผลฉันไม่แน่ใจว่ามีประสิทธิภาพเพียงใด ...


นี่ไม่ได้เอาอะไรออกจากกระเป๋า สิ่งของที่คุณกำลังเรียกค้นยังคงอยู่ในกระเป๋า
Keith

3
ควรจะเป็น "bag = new ConcurrentBag (bag.Where (x => x! = item))"
atikot

4
@atikot บรรทัดนั้นทำให้ฉันหัวเราะ
parek
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.