ConcurrentBag如何才能删除指定的某个元素?

sgyiliya 2018-02-26 02:11:52


public class Test
{
public string isok;
public string isfine;
}

ConcurrentBag<Test> MyTest = new ConcurrentBag<Test>();

private void button2_Click(object sender, EventArgs e)
{
for(int i=0;i<3;i++)
{
Test TestEntity = new Test();
TestEntity.isok = "A" + i.ToString();
TestEntity.isfine = "B" + i.ToString();
MyTest.Add(TestEntity);
}

Test ttt = MyTest.First(p => p.isok.Equals("A1") == true);
if (ttt != null)
{
MyTest.TryTake(out ttt);
}
}




代码如上,TryTake无法删除指定的isok=A1的那个元素。

请问有什么办法能指定删除这个isok=A1的那个元素吗?
...全文
3149 24 打赏 收藏 转发到动态 举报
写回复
用AI写文章
24 条回复
切换为时间正序
请发表友善的回复…
发表回复
干拉痞痞虾 2019-04-19
  • 打赏
  • 举报
回复
引用 11 楼 sgyiliya 的回复:
初步结论看来是, 1,trytake不能删除指定的元素,估计ConcurrentQueue,ConcurrentStack ,ConcurrentBag,BlockingCollection,这些都不能做到这点。 2,用ConcurrentDictionary,可以删除指定的元素。也可以实现线程安全的集合(list)的一些功能,确实可以减少锁的使用,但是无法做到完全不用锁,该用锁的地方还是必须要用锁。
那什么情况下,还是需要用到锁的
正怒月神 2018-02-27
  • 打赏
  • 举报
回复
那就使用 currentdictionary吧。可能也就这个合适了。 除非你自己使用lock和list
bloodish 2018-02-27
  • 打赏
  • 举报
回复
引用 20 楼 hanjun0612 的回复:
[quote=引用 19 楼 bloodish 的回复:] 版主大人,我说的是不是移除特定的对象,我没有否认TryTake移除对象的功能, 其实我前面早就有回复. 你的代码可以自己执行看一下,如果你觉得没有问题,那我可以帮你执行.
哦,我知道了,你意思是出栈不是指定的那个。 那倒是我想当然了。可能需要换一个集合类型来使用了。[/quote] 是的,TryTake返回值是bool类型,out 带出的是被删除那个对象, 这个参数不是用来做删除比较的. 楼主的问题在纠结是否存在循环(for,foreach)时仍然线程安全的列表数据结构, 这个没想到有什么现成的. 并发集合类型只能保证类型对象本身的操作满足线程安全.
正怒月神 2018-02-27
  • 打赏
  • 举报
回复
引用 19 楼 bloodish 的回复:
版主大人,我说的是不是移除特定的对象,我没有否认TryTake移除对象的功能, 其实我前面早就有回复. 你的代码可以自己执行看一下,如果你觉得没有问题,那我可以帮你执行.
哦,我知道了,你意思是出栈不是指定的那个。 那倒是我想当然了。可能需要换一个集合类型来使用了。
bloodish 2018-02-27
  • 打赏
  • 举报
回复
引用 18 楼 hanjun0612 的回复:
[quote=引用 16 楼 bloodish 的回复:] 这是错误的方法, TryTake根本不是用来移除特定的对象的。
不清楚为何你这么说。 msdn是这么说的 [/quote] 版主大人,我说的是不是移除特定的对象,我没有否认TryTake移除对象的功能, 其实我前面早就有回复. 你的代码可以自己执行看一下,如果你觉得没有问题,那我可以帮你执行.
正怒月神 2018-02-27
  • 打赏
  • 举报
回复
引用 16 楼 bloodish 的回复:
这是错误的方法, TryTake根本不是用来移除特定的对象的。

不清楚为何你这么说。
msdn是这么说的
bloodish 2018-02-27
  • 打赏
  • 举报
回复
根据楼主的需求,典型的生产者-消费者场景,但我不能肯定你的Cache什么时候被消费, 我举个BlockingCollection的例子,满足你的foreach,不需要加锁, BlockingCollection默认是按队列方式处理,也可以指定其他的集合类型。 具体怎么用,你自己可以举一反三

static void Main(string[] args)
        {
            BlockingCollection<string> cachedQueue= new BlockingCollection<string>();
            AutoResetEvent stopAddEvent = new AutoResetEvent(false);

            //start producer task
            Task.Factory.StartNew(() =>
            {
                Console.WriteLine($"Start add item:{DateTime.Now:yyyy-MM-dd,hh:mm:ss,fff}");
                while (!stopAddEvent.WaitOne(100))
                {
                    cachedQueue.Add($"{Guid.NewGuid()}");
                }
                cachedQueue.CompleteAdding();
                Console.WriteLine($"Complete add item:{DateTime.Now:yyyy-MM-dd,hh:mm:ss,fff}");
            });

            //start comsumer task
            Task.Factory.StartNew(() =>
            {
                foreach(var item in cachedQueue.GetConsumingEnumerable())
                {
                    Console.WriteLine($"Consuming {item}");
                }
                Console.WriteLine($"All items consumed:{DateTime.Now:yyyy-MM-dd,hh:mm:ss,fff}");
            });

            //stop produce item after 5s
            Task.Factory.StartNew(() =>
            {
                Thread.Sleep(5000);
                Console.WriteLine($"Will stop add item:{DateTime.Now:yyyy-MM-dd,hh:mm:ss,fff}");
                stopAddEvent.Set();
            });

            Console.Read();
        }
bloodish 2018-02-27
  • 打赏
  • 举报
回复
引用 15 楼 hanjun0612 的回复:
public class User
        {
            public int id { get; set; }
        }

        static void Main(string[] args)
        {
            //测试数据
            ConcurrentBag<User> list = new ConcurrentBag<User>();

            User u1=new User(){id=1};
            User u2=new User(){id=2};
            User u3=new User(){id=3};

            list.Add(u1);
            list.Add(u2);
            list.Add(u3);

            //3个
            Console.WriteLine(list.Count);

            //移除id=1的
            User u=new User() { id = 1 };
            list.TryTake(out u);

            //2个
            Console.WriteLine(list.Count);

            Console.ReadLine();
        }
//移除id=1的 User u=new User() { id = 1 }; list.TryTake(out u); 这是错误的方法, TryTake根本不是用来移除特定的对象的。
正怒月神 2018-02-27
  • 打赏
  • 举报
回复
public class User
        {
            public int id { get; set; }
        }

        static void Main(string[] args)
        {
            //测试数据
            ConcurrentBag<User> list = new ConcurrentBag<User>();

            User u1=new User(){id=1};
            User u2=new User(){id=2};
            User u3=new User(){id=3};

            list.Add(u1);
            list.Add(u2);
            list.Add(u3);

            //3个
            Console.WriteLine(list.Count);

            //移除id=1的
            User u=new User() { id = 1 };
            list.TryTake(out u);

            //2个
            Console.WriteLine(list.Count);

            Console.ReadLine();
        }
秋的红果实 2018-02-27
  • 打赏
  • 举报
回复
发错了, 我写的就是删除指定元素啊! var temp1 = MyTest.Where(t => t.isok != "A1"); foreach(Test t in temp1) { MessageBox.Show(t.isok.ToString() + "-" + t.isfine.ToString()); } 指定的是A1,执行后,输出A0,A2,难道这不是删除了指定元素A1 !!!
sgyiliya 2018-02-27
  • 打赏
  • 举报
回复


//说明:PAIDMergeStatus是自定义的类,也就是变通实现线程安全的List<PAIDMergeStatus>
//PAIDMergeStatus类中必须要有一个int类型的唯一值的字段,这个字段作为ConcurrentDictionary的key
    public class ClassForLock
    {
        public static ConcurrentDictionary<int, PAIDMergeStatus> _PAIDMergeStatus = new ConcurrentDictionary<int, PAIDMergeStatus>();

        public static List<T> GetAllFromCache<T>()
        {
            if (typeof(T) == typeof(PAIDMergeStatus))
            {
                return _PAIDMergeStatus.Values.ToList() as List<T>;
            }

            return null;
        }

        public static void AddOrUpdateForPA<T>(T _Data)
        {
            if (typeof(T) == typeof(PAIDMergeStatus))
            {
                if (_PAIDMergeStatus != null && _Data != null)
                {
                    PAIDMergeStatus _Temp = _Data as PAIDMergeStatus;
                    _PAIDMergeStatus.AddOrUpdate(_Temp.PAID, _Temp, (key, oldvalue) => _Temp);
                }
            }
        }

        public static void AddOrUpdateForPA<T>(List<T> _List_Data)
        {
            foreach (T _Data in _List_Data)
            {
                AddOrUpdateForPA<T>(_Data);
            }
        }

        public static void TryRemoveForPA<T>(int sysid)
        {
            if (typeof(T) == typeof(PAIDMergeStatus))
            {
                if (_PAIDMergeStatus != null)
                {
                    PAIDMergeStatus temp = null;
                    _PAIDMergeStatus.TryRemove(sysid, out temp);
                }
            }
        }

        public static void TryRemoveForPA<T>(List<int> sysid)
        {
            for (int i = 0; i < sysid.Count; i++)
            {
                TryRemoveForPA<T>(sysid[i]);
            }
        }


        public static void Clear(string _str)
        {
            if (_str.Equals("PAIDMergeStatus"))
            {
                _PAIDMergeStatus.Clear();
            }
        }

        public static void OrderByID(string _str)
        {
            if (_str.Equals("PAIDMergeStatus"))
            {
                List<PAIDMergeStatus> _TempOrderBy = GetAllFromCache<PAIDMergeStatus>().OrderBy(p => p.PAID).ToList() as List<PAIDMergeStatus>;
                Clear("PAIDMergeStatus");
                AddOrUpdateForPA<PAIDMergeStatus>(_TempOrderBy);
            }
        }

    }

上面是我自己用的ConcurrentDictionary的办法, 但是存在问题:当我需要对List<PAIDMergeStatus>执行foreach或者for循环的时候,还是必须要用锁, 对这个问题,你们有解决办法吗? 就是因为ConcurrentDictionary还不够完美,所以我才寻求ConcurrentBag有没有完美的办法,看来我的寻求是失败了。
秋的红果实 2018-02-27
  • 打赏
  • 举报
回复
Test ttt = MyTest.First(p => p.isok.Equals("A1") == true); if (ttt != null) { MyTest.TryTake(out ttt); } ==> Test ttt; MyTest.TryTake(out ttt); 确实是从
引用 10 楼 sgyiliya 的回复:
[quote=引用 8 楼 From_TaiWan 的回复:] lz为什么不用where?

ConcurrentBag<Test> MyTest = new ConcurrentBag<Test>();
for (int i = 0; i < 3; i++)
{
    Test TestEntity = new Test();
    TestEntity.isok = "A" + i.ToString();
    TestEntity.isfine = "B" + i.ToString();
    MyTest.Add(TestEntity);
}

var temp1 = MyTest.Where(t => t.isok != "A1");
foreach(Test t in temp1)
{
    MessageBox.Show(t.isok.ToString() + "-" + t.isfine.ToString());
}

输出可以达到你的要求
这个不能满足要求啊,要求是必须删除指定的元素。[/quote]我写的就是删除指定元素啊! var temp1 = MyTest.Where(t => t.isok != "A1"); foreach(Test t in temp1) { MessageBox.Show(t.isok.ToString() + "-" + t.isfine.ToString()); } 指定的是A1,执行后,输出A0,A2,难道这不是删除了指定元素A1 !!!
sgyiliya 2018-02-27
  • 打赏
  • 举报
回复
初步结论看来是, 1,trytake不能删除指定的元素,估计ConcurrentQueue,ConcurrentStack ,ConcurrentBag,BlockingCollection,这些都不能做到这点。 2,用ConcurrentDictionary,可以删除指定的元素。也可以实现线程安全的集合(list)的一些功能,确实可以减少锁的使用,但是无法做到完全不用锁,该用锁的地方还是必须要用锁。
sgyiliya 2018-02-27
  • 打赏
  • 举报
回复
引用 7 楼 DOwnstairs 的回复:
建议用ConcurrentDictionary<string, Test> 另外Concurrent系列并不能保证多线程的正确性,他只能保证多线程操作的时候不会产生内存错误,需要加锁的地方还是需要加锁 他能保证的是读取和写入的时候线程安全,但你肯定有相关逻辑代码需要自己再加锁。。。我踩过坑。。
好的,看来锁还是要用。
sgyiliya 2018-02-27
  • 打赏
  • 举报
回复
引用 8 楼 From_TaiWan 的回复:
lz为什么不用where?

ConcurrentBag<Test> MyTest = new ConcurrentBag<Test>();
for (int i = 0; i < 3; i++)
{
    Test TestEntity = new Test();
    TestEntity.isok = "A" + i.ToString();
    TestEntity.isfine = "B" + i.ToString();
    MyTest.Add(TestEntity);
}

var temp1 = MyTest.Where(t => t.isok != "A1");
foreach(Test t in temp1)
{
    MessageBox.Show(t.isok.ToString() + "-" + t.isfine.ToString());
}

输出可以达到你的要求
这个不能满足要求啊,要求是必须删除指定的元素。
秋的红果实 2018-02-27
  • 打赏
  • 举报
回复
哦,我#8写的确实存在类型问题,改下

private ConcurrentBag<Test> removedAt(ConcurrentBag<Test> originalCon,string key)
{
    ConcurrentBag<Test> result = new ConcurrentBag<Test>();
    var temp=originalCon.Where<Test>(t => t.isok != key);
    foreach(var t in temp)
    {
        result.Add(new Test() { isok = t.isok, isfine = t.isfine });
    }
    return result;

}

//调用
private void button1_Click(object sender, EventArgs e)
{
    ConcurrentBag<Test> MyTest = new ConcurrentBag<Test>();
    for (int i = 0; i < 3; i++)
    {
        Test TestEntity = new Test();
        TestEntity.isok = "A" + i.ToString();
        TestEntity.isfine = "B" + i.ToString();
        MyTest.Add(TestEntity);
    }

    //调用removedAt()后,返回的还是concurrentBag对象
    ConcurrentBag<Test> removedColl = removedAt(MyTest, "A1");
    foreach (Test t in removedColl)
    {
        MessageBox.Show(t.isok.ToString() + "-" + t.isfine.ToString());
    }
}

这回完全符合lz的要求:删除指定元素。在原来的基础上继续工作,应该最省事
秋的红果实 2018-02-26
  • 打赏
  • 举报
回复
lz为什么不用where?

ConcurrentBag<Test> MyTest = new ConcurrentBag<Test>();
for (int i = 0; i < 3; i++)
{
    Test TestEntity = new Test();
    TestEntity.isok = "A" + i.ToString();
    TestEntity.isfine = "B" + i.ToString();
    MyTest.Add(TestEntity);
}

var temp1 = MyTest.Where(t => t.isok != "A1");
foreach(Test t in temp1)
{
    MessageBox.Show(t.isok.ToString() + "-" + t.isfine.ToString());
}

输出可以达到你的要求
SoulRed 2018-02-26
  • 打赏
  • 举报
回复
建议用ConcurrentDictionary<string, Test> 另外Concurrent系列并不能保证多线程的正确性,他只能保证多线程操作的时候不会产生内存错误,需要加锁的地方还是需要加锁 他能保证的是读取和写入的时候线程安全,但你肯定有相关逻辑代码需要自己再加锁。。。我踩过坑。。
  • 打赏
  • 举报
回复
2L代码已经给你写好了……
sgyiliya 2018-02-26
  • 打赏
  • 举报
回复
引用 4 楼 bloodish 的回复:
ConcurentBag是无序的并发集合. TryTake只是移除最后一个element, 移除的element赋值给out 指定的参数. 这个参数不是用来做比较的, HashSet其实挺合适的,但如果你一定要用并发集合,那么可以选择ConcurrentDictionary<string, Test>, 用isOK作为Key
感谢。 但是字典似乎有些不好用,我记得好像是for循环没法做到线程安全。 我刚才又百度,找到这份代码,麻烦你们高手鉴定一下,这份代码有没有问题?我初步测了,似乎没问题。 https://github.com/piercep/thread-safe-collections/blob/master/ThreadSafeCollections/ThreadSafeCollections/TList.cs
加载更多回复(4)

110,502

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Web++
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧