C# 多线程安全的无序集合ConcurrentBag

勇哥注:

ConcurrentBag是一个线程安全的无序集合。专为生产消费模式进行订制的集合。

如果多线程使用List<T>就会遇到问题:

System.InvalidOperationException:“集合已修改;可能无法执行枚举操作。”。

原因是timer2在遍历list的过程当中,timer1修改了list,使其大小发生了变化。

如果使用ConcurrentBag这类安全集合,可以避免这类问题出现。


它的常见方法如下:

Add         添加一个元素
TryPeek     方法返回一个元素,并且不删除原集合中的元素。
TryTake     返回元素并移除
FirstOrDefault      返回指定的元素

无论是TryPeek还是TryTake 都只是按先入先出取出数据。你想中间删除一个元素是做不到的。


用惯了List<T>,相比之下它的缺点如下:

  1.    不能下标访问,如data[0]

  2.   没有RmoveAt方法 

      即你没有办法删除到指定的元素


对于第2点,特别说明如下:


ConcurrentBag为每个线程保留一个线程本地队列,并且只有在其自己的队列变空时才查看其他线程的队列。如果您删除了一个项目并将其放回原来,那么您删除的下一个项目可能会再次成为同一个项目。无法保证重复删除项目并将其放回将允许您迭代所有项目。


两种替代方案:


  • 删除所有项目并记住它们,直到找到要删除的项目,然后将其他项目放回原处。请注意,如果两个线程同时尝试执行此操作,则会出现问题。

  • 使用更合适的数据结构,例如ConcurrentDictionary。


using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        ConcurrentBag<string> buffer = new ConcurrentBag<string>();
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            buffer.Add("windows32");
            buffer.Add("linux");
            buffer.Add("unix");
            buffer.Add("os/2");
            buffer.Add("windowsxp");
            buffer.Add("win7");
            buffer.Add("win10");
          
            updateui();
           
        }

        private void updateui()
        {
         
            listBox1.DataSource = null;
            var list2 = buffer.ToList();
            list2.Reverse();
            listBox1.DataSource = list2;
        }

        private void button2_Click(object sender, EventArgs e)
        {
            //取一个元素
            string item1;
            buffer.TryPeek(out item1);
            richTextBox1.AppendText(item1);
            updateui();
            //可以看到,取出的是尾巴上的元素
        }

        private void button3_Click(object sender, EventArgs e)
        {
            //取一个元素并删除
            string item1;
            buffer.TryTake(out item1);
            richTextBox1.AppendText(item1);
            updateui();
            //可以看到,取出的是尾巴上的元素
        }

        private void button4_Click(object sender, EventArgs e)
        {
            //取指定索引的元素
            var selitem=buffer.FirstOrDefault(s => s == textBox1.Text);
            if(selitem==null)
            {
                MessageBox.Show($"找不到 {textBox1.Text}");return;
            }
            richTextBox1.AppendText(selitem);
            updateui();
        }

        private void button5_Click(object sender, EventArgs e)
        {
            MessageBox.Show("请相信,你想既不损失效率的情况,又想轻易做到这一点,是不可能的。\n如果你正想这样做,那么你应该选择其它的数据结构,例如concurrentDictionary");
         
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            listBox1.Items.Clear();
        }
    }
}


image.png



测试代码下载:

支付1元或购买VIP会员后,才能查看本内容!立即支付升级会员查询订单



--------------------- 

作者:hackpig

来源:www.skcircle.com

版权声明:本文为博主原创文章,转载请附上博文链接!


本文出自勇哥的网站《少有人走的路》wwww.skcircle.com,转载请注明出处!讨论可扫码加群:

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

会员中心
搜索
«    2024年5月    »
12345
6789101112
13141516171819
20212223242526
2728293031
网站分类
标签列表
最新留言
    热门文章 | 热评文章 | 随机文章
文章归档
友情链接
  • 订阅本站的 RSS 2.0 新闻聚合
  • 扫描加本站机器视觉QQ群,验证答案为:halcon勇哥的机器视觉
  • 点击查阅微信群二维码
  • 扫描加勇哥的非标自动化群,验证答案:C#/C++/VB勇哥的非标自动化群
  • 扫描加站长微信:站长微信:abc496103864
  • 扫描加站长QQ:
  • 扫描赞赏本站:
  • 留言板:

Powered By Z-BlogPHP 1.7.2

Copyright Your skcircle.com Rights Reserved.

鄂ICP备18008319号


站长QQ:496103864 微信:abc496103864