C#线程安全集合类说明(1): BlockingCollection<T>

线程安全的集合所在的命名空间 using System.Collections.Concurrent;

Concurrent意思是并发的,并行的。反义是sequential(顺序的),线程安全的意思就是多线程中的同步

System.Collections.Concurrent 命名空间

System.Collections.Concurrent 命名空间提供多个线程安全集合类。当有多个线程并发访问集合时,应使用这些类代替 System.Collections 和 System.Collections.Generic 命名空间中的对应类型。 但是,不保证通过扩展方法或通过显式接口实现访问集合对象是线程安全的,可能需要由调用方进行同步。

BlockingCollection<T>

为实现 IProducerConsumerCollection<T> 的线程安全集合提供阻塞和限制功能。

ConcurrentBag<T>

表示对象的线程安全的无序集合。

ConcurrentDictionary<TKey,TValue>

表示可由多个线程同时访问的键/值对的线程安全集合。

ConcurrentQueue<T>

表示线程安全的先进先出 (FIFO) 集合。

ConcurrentStack<T>

表示线程安全的后进先出 (LIFO) 集合。

OrderablePartitioner<TSource>

表示将可排序数据源拆分为多个分区的特定方式。

Partitioner

提供针对数组、列表和可枚举项的常见分区策略。

Partitioner<TSource>

表示将数据源拆分为多个分区的特定方式。

接口

接口
IProducerConsumerCollection<T>

定义供制造者/使用者用来操作线程安全集合的方法。 此接口提供一个统一的表示(为生产者/消费者集合),从而更高级别抽象如 BlockingCollection<T> 可以使用集合作为基础的存储机制。

枚举

枚举
EnumerablePartitionerOptions

指定控制分区程序的缓冲行为的选项。

 

参考微软官方文档:

https://docs.microsoft.com/zh-cn/dotnet/api/system.collections.concurrent?view=netframework-4.5

参考源代码(类BlockingCollection<T>)

https://referencesource.microsoft.com/#System/sys/system/collections/concurrent/BlockingCollection.cs,0c84922eea8bf511

一、示例:BlockingCollection<T> ,该类与队列ConcurrentQueue<T>类似。

源程序:

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace ThreadSafeCollectionDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("测试阻塞集合BlockingCollection的添加【Add】和移除【Take】...");
            Task task = AddTakeBlockingCollectionAsync();
            Task.WaitAll(task);
            Console.WriteLine("下面测试阻塞集合BlockingCollection的尝试移除【TryTake】...");
            TryTakeBlockingCollection();
            Console.ReadLine();
        }

        /// <summary>
        /// 阻塞集合BlockingCollection的Add(添加)和Take(移除):生产者【添加元素】、消费者【移除元素】线程安全同时操作
        /// </summary>
        /// <returns></returns>
        static async Task AddTakeBlockingCollectionAsync()
        {
            using (BlockingCollection<int> blockingCollection = new BlockingCollection<int>())
            {
                //生产者
                Task taskProducer = Task.Run(() => 
                {
                    //添加一个元素
                    blockingCollection.Add(1);
                    blockingCollection.Add(2);
                    blockingCollection.Add(3);
                    //结束添加元素:集合已标记为完成添加。CompleteAdding()方法会导致Take()方法出现移除操作异常
                    blockingCollection.CompleteAdding();
                });
                //消费者
                Task taskConsumer = Task.Factory.StartNew(() => 
                {
                    try
                    {
                        while (true)
                        {
                            //移除一个元素,并返回这个移除的元素
                            int removeItem = blockingCollection.Take();
                            Console.WriteLine(removeItem);
                        }
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine($"移除项出现异常:{ex.Message}");
                    }
                });
                await Task.WhenAll(taskProducer, taskConsumer);
            }
        }

        /// <summary>
        /// 尝试移除阻塞集合BlockingCollection中的元素
        /// </summary>
        static void TryTakeBlockingCollection()
        {
            using (BlockingCollection<int> bc = new BlockingCollection<int>())
            {
                for (int i = 0; i < 1000; i++)
                {
                    bc.Add(i);
                }
                Console.WriteLine($"是否完成添加:【{bc.IsAddingCompleted}】");
                bc.CompleteAdding();//标记集合已经完成添加
                Console.WriteLine($"是否完成添加:【{bc.IsAddingCompleted}】");

                int outerSum = 0;
                Action action = new Action(() =>
                {
                    int localItem;
                    int sum = 0;
                    while (bc.TryTake(out localItem))
                    {
                        sum += localItem;
                    }
                    Interlocked.Add(ref outerSum, sum);
                    Console.WriteLine($"求和Sum={sum},当前替换:【{outerSum}】");
                });

                //并行操作:三个委托同时执行
                //相当于 将数组求和 分 三小部分数组 分别求和,最后合并
                Parallel.Invoke(action, action, action);
                Console.WriteLine($"此时outerSum为【{outerSum}】");
                Console.WriteLine($"此时集合已完成添加并且集合为空:【{bc.IsCompleted}】");
            }
        }

    }
}

程序运行如图:

 

 

 

 

 

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
上传项目不支持Firefox,提示代码附件太大(1.4M),我写了30多分钟的描述全没了,太坑爹了。 10分有点贵,绝对原创,共2个代码文件300多行,下载请谨慎。你下载了,若绝对不爽在评论中说出来,不要让其他同学上当,如果觉得还可以也请留言。 代码采用多工作者多线程执行任务。通过暴露的方法往工作者传递消息,然后采用事件回调返回处理结果,实现的事件有OnThreadComplete,OnAddedTask,OnStart,OnSuccess,OnFailure,OnTimeout。 事件回调支持同步或异步,每工作者可以指定执行超时时间,避免线程阻塞死掉。队列采用线程安全BlockingCollection,每组工作者用一个队列。委托采用Func来定义的,没有采用传统且不太好理解的Delegate。这让代码减少很多,也更容易理解。多线程应该采用消息中心来交换数据,这样就规避了线程同步交互,等待,阻塞等等,全部是异步调用,全部是接收消息工作,然后产生消息,线程间没有耦合,消息中心有很多成熟的方案如RabbitMQ, Redis(里面有简单的消息交换),微软有消息云服务等。如果应用不复杂,可以采用DB做个简单的消息中心,建议采用HTTP接口来获取与写入消息,方便将来升级重构消息中心。 开发环境VS2012,Framework4.0,代码注释量很大,如果你高兴这代码你可以随意蹂躏,如果你有建设性意见请告诉我。 下面是部分测试代码: //发送消息方法容器 var msgContainer = new Hashtable(); //创建并启动工作者 foreach (var key in workers.Keys) { //创建工作者 //启动5个线程,异步事件回调,方法执行20秒超时,程序跑起来有100个线程,由于引入超时控制,实际线程将达100+50 //下面的20个工作组,有5个是超时的,主要测试OnTimeout事件,你可以设置seleep的时间来控制 //我把sleep的时间设置的有点长,方便你测试 //测试的时候你会看见有异常,那是应为Timeout我采用的是Thread.Abort方法,这样才出发了ontimeout事件 var worker = new Sehui.Worker(5, key.ToString(), (Func)workers[key], false, new TimeSpan(0, 0, 20)); worker.OnStart += worker_OnEvent; worker.OnSuccess += worker_OnEvent; worker.OnFailure += worker_OnEvent; worker.OnTimeout += worker_OnEvent; //启动工作者 worker.Start(); //将增加消息方法放到Hashtable中 //这里我是偷懒,下面可以用循环的方式往线程中add message msgContainer.Add(key.ToString(), new Func(worker.AddTask)); } //向20个工作者发送消息,每个工作者发送20条消息 for (var i = 0; i < 20; i++) { for (var k = 0; k < 20; k++) { ((Func)msgContainer["SyncDb" + k])("[Work " + k + "] Message " + i); Console.WriteLine("send msg to worker{0},msgid:{1}", k, i); } }

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

斯内科

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值