这篇是并发集合中的最后一篇,介绍一下BlockingCollection。在工作中我还没有使用过,但是看上去应该是为了便捷使用并发集合而创建的类型。默认情况下,BlockingCollection使用的是ConcurrentQueue容器,当然我们也可以使用其他实现了IProducerConsumerCollection的类型来操作。

static Task GetRandomDelay()
{
int delay = new Random(DateTime.Now.Millisecond).Next(1, 500);
return Task.Delay(delay);
} class CustomTask
{
public int Id { get; set; }
} static async Task RunProgram(IProducerConsumerCollection<CustomTask> collection = null)
{
var taskCollection = new BlockingCollection<CustomTask>();
if(collection != null)
taskCollection= new BlockingCollection<CustomTask>(collection); var taskSource = Task.Run(() => TaskProducer(taskCollection)); Task[] processors = new Task[4];
for (int i = 1; i <= 4; i++)
{
string processorId = "Processor " + i;
processors[i - 1] = Task.Run(() => TaskProcessor(taskCollection, processorId));
} await taskSource; await Task.WhenAll(processors);
} static async Task TaskProducer(BlockingCollection<CustomTask> collection)
{
for (int i = 1; i <= 20; i++)
{
await Task.Delay(20);
var workItem = new CustomTask { Id = i };
collection.Add(workItem);
Console.WriteLine("Task {0} has been posted", workItem.Id);
}
collection.CompleteAdding(); //完成工作
} static async Task TaskProcessor(BlockingCollection<CustomTask> collection, string name)
{
await GetRandomDelay();
foreach (CustomTask item in collection.GetConsumingEnumerable())
{
Console.WriteLine("Task {0} has been processed by {1}", item.Id, name);
await GetRandomDelay();
}
} 首先调用默认的BlockingCollection: 然后我们传入一个ConcurrentStack实例 Console.WriteLine("Using a Stack inside of BlockingCollection");
Console.WriteLine();
Task t = RunProgram(new ConcurrentStack<CustomTask>());
t.Wait();
C# 并行编程 之 并发集合 (.Net Framework 4.0)
2015年05月08日 10:15:29 zy__ 阅读数 24909更多
分类专栏: C#
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/wangzhiyu1980/article/details/45497907
此文为个人学习《C#并行编程高级教程》的笔记,总结并调试了一些文章中的代码示例。 在以后开发过程中可以加以运用。 对于并行任务,与其相关紧密的就是对一些共享资源,数据结构的并行访问。经常要做的就是对一些队列进行加锁-解锁,然后执行类似插入,删除等等互斥操作。 .NetFramework 4.0 中提供了一些封装好的支持并行操作数据容器,可以减少并行编程的复杂程度。 基本信息
.NetFramework中并行集合的名字空间: System.Collections.Concurrent 并行容器: ConcurrentQueue
ConcurrentStack
ConcurrentBag : 一个无序的数据结构集,当不需要考虑顺序时非常有用。
BlockingCollection : 与经典的阻塞队列数据结构类似
ConcurrentDictionary 这些集合在某种程度上使用了无锁技术(CAS Compare-and-Swap和内存屏障 Memory Barrier),与加互斥锁相比获得了性能的提升。但在串行程序中,最好不用这些集合,它们必然会影响性能。 关于CAS:
http://www.tuicool.com/articles/zuui6z
http://www.360doc.com/content/11/0914/16/7656248_148221200.shtml
关于内存屏障
http://en.wikipedia.org/wiki/Memory_barrier 用法与示例
ConcurrentQueue
其完全无锁,但当CAS面临资源竞争失败时可能会陷入自旋并重试操作。 Enqueue:在队尾插入元素
TryDequeue:尝试删除队头元素,并通过out参数返回
TryPeek:尝试将对头元素通过out参数返回,但不删除该元素。 程序示例: using System;
using System.Text; using System.Threading.Tasks;
using System.Collections.Concurrent; namespace Sample4_1_concurrent_queue
{
class Program
{
internal static ConcurrentQueue<int> _TestQueue; class ThreadWork1 // producer
{
public ThreadWork1()
{ } public void run()
{
System.Console.WriteLine("ThreadWork1 run { ");
for (int i = 0; i < 100; i++)
{
System.Console.WriteLine("ThreadWork1 producer: " + i);
_TestQueue.Enqueue(i);
}
System.Console.WriteLine("ThreadWork1 run } ");
}
} class ThreadWork2 // consumer
{
public ThreadWork2()
{ } public void run()
{
int i = 0;
bool IsDequeuue = false;
System.Console.WriteLine("ThreadWork2 run { ");
for (; ; )
{
IsDequeuue = _TestQueue.TryDequeue(out i);
if (IsDequeuue)
System.Console.WriteLine("ThreadWork2 consumer: " + i * i + " ====="); if (i == 99)
break;
}
System.Console.WriteLine("ThreadWork2 run } ");
}
} static void StartT1()
{
ThreadWork1 work1 = new ThreadWork1();
work1.run();
} static void StartT2()
{
ThreadWork2 work2 = new ThreadWork2();
work2.run();
}
static void Main(string[] args)
{
Task t1 = new Task(() => StartT1());
Task t2 = new Task(() => StartT2()); _TestQueue = new ConcurrentQueue<int>(); Console.WriteLine("Sample 3-1 Main {"); Console.WriteLine("Main t1 t2 started {");
t1.Start();
t2.Start();
Console.WriteLine("Main t1 t2 started }"); Console.WriteLine("Main wait t1 t2 end {");
Task.WaitAll(t1, t2);
Console.WriteLine("Main wait t1 t2 end }"); Console.WriteLine("Sample 3-1 Main }"); Console.ReadKey();
}
}
} ConcurrentStack
其完全无锁,但当CAS面临资源竞争失败时可能会陷入自旋并重试操作。 Push:向栈顶插入元素
TryPop:从栈顶弹出元素,并且通过out 参数返回
TryPeek:返回栈顶元素,但不弹出。 程序示例: using System;
using System.Text; using System.Threading.Tasks;
using System.Collections.Concurrent; namespace Sample4_2_concurrent_stack
{
class Program
{
internal static ConcurrentStack<int> _TestStack; class ThreadWork1 // producer
{
public ThreadWork1()
{ } public void run()
{
System.Console.WriteLine("ThreadWork1 run { ");
for (int i = 0; i < 100; i++)
{
System.Console.WriteLine("ThreadWork1 producer: " + i);
_TestStack.Push(i);
}
System.Console.WriteLine("ThreadWork1 run } ");
}
} class ThreadWork2 // consumer
{
public ThreadWork2()
{ } public void run()
{
int i = 0;
bool IsDequeuue = false;
System.Console.WriteLine("ThreadWork2 run { ");
for (; ; )
{
IsDequeuue = _TestStack.TryPop(out i);
if (IsDequeuue)
System.Console.WriteLine("ThreadWork2 consumer: " + i * i + " =====" + i); if (i == 99)
break;
}
System.Console.WriteLine("ThreadWork2 run } ");
}
} static void StartT1()
{
ThreadWork1 work1 = new ThreadWork1();
work1.run();
} static void StartT2()
{
ThreadWork2 work2 = new ThreadWork2();
work2.run();
}
static void Main(string[] args)
{
Task t1 = new Task(() => StartT1());
Task t2 = new Task(() => StartT2()); _TestStack = new ConcurrentStack<int>(); Console.WriteLine("Sample 4-1 Main {"); Console.WriteLine("Main t1 t2 started {");
t1.Start();
t2.Start();
Console.WriteLine("Main t1 t2 started }"); Console.WriteLine("Main wait t1 t2 end {");
Task.WaitAll(t1, t2);
Console.WriteLine("Main wait t1 t2 end }"); Console.WriteLine("Sample 4-1 Main }"); Console.ReadKey();
}
}
} 测试中一个有趣的现象: 虽然生产者已经在栈中插入值已经到了25,但消费者第一个出栈的居然是4,而不是25。很像是出错了。但仔细想想入栈,出栈和打印语句是两个部分,而且并不是原子操作,出现这种现象应该也算正常。 Sample 3-1 Main {
Main t1 t2 started {
Main t1 t2 started }
Main wait t1 t2 end {
ThreadWork1 run {
ThreadWork1 producer: 0
ThreadWork2 run {
ThreadWork1 producer: 1
ThreadWork1 producer: 2
ThreadWork1 producer: 3
ThreadWork1 producer: 4
ThreadWork1 producer: 5
ThreadWork1 producer: 6
ThreadWork1 producer: 7
ThreadWork1 producer: 8
ThreadWork1 producer: 9
ThreadWork1 producer: 10
ThreadWork1 producer: 11
ThreadWork1 producer: 12
ThreadWork1 producer: 13
ThreadWork1 producer: 14
ThreadWork1 producer: 15
ThreadWork1 producer: 16
ThreadWork1 producer: 17
ThreadWork1 producer: 18
ThreadWork1 producer: 19
ThreadWork1 producer: 20
ThreadWork1 producer: 21
ThreadWork1 producer: 22
ThreadWork1 producer: 23
ThreadWork1 producer: 24
ThreadWork1 producer: 25
ThreadWork2 consumer: 16 =====4
ThreadWork2 consumer: 625 =====25
ThreadWork2 consumer: 576 =====24
ThreadWork2 consumer: 529 =====23
ThreadWork1 producer: 26
ThreadWork1 producer: 27
ThreadWork1 producer: 28 ConcurrentBag
一个无序的集合,程序可以向其中插入元素,或删除元素。
在同一个线程中向集合插入,删除元素的效率很高。 Add:向集合中插入元素 TryTake:从集合中取出元素并删除 TryPeek:从集合中取出元素,但不删除该元素。 程序示例: using System;
using System.Text; using System.Threading.Tasks;
using System.Collections.Concurrent; namespace Sample4_3_concurrent_bag
{
class Program
{
internal static ConcurrentBag<int> _TestBag; class ThreadWork1 // producer
{
public ThreadWork1()
{ } public void run()
{
System.Console.WriteLine("ThreadWork1 run { ");
for (int i = 0; i < 100; i++)
{
System.Console.WriteLine("ThreadWork1 producer: " + i);
_TestBag.Add(i);
}
System.Console.WriteLine("ThreadWork1 run } ");
}
} class ThreadWork2 // consumer
{
public ThreadWork2()
{ } public void run()
{
int i = 0;
int nCnt = 0;
bool IsDequeuue = false;
System.Console.WriteLine("ThreadWork2 run { ");
for (;;)
{
IsDequeuue = _TestBag.TryTake(out i);
if (IsDequeuue)
{
System.Console.WriteLine("ThreadWork2 consumer: " + i * i + " =====" + i);
nCnt++;
} if (nCnt == 99)
break;
}
System.Console.WriteLine("ThreadWork2 run } ");
}
} static void StartT1()
{
ThreadWork1 work1 = new ThreadWork1();
work1.run();
} static void StartT2()
{
ThreadWork2 work2 = new ThreadWork2();
work2.run();
}
static void Main(string[] args)
{
Task t1 = new Task(() => StartT1());
Task t2 = new Task(() => StartT2()); _TestBag = new ConcurrentBag<int>(); Console.WriteLine("Sample 4-3 Main {"); Console.WriteLine("Main t1 t2 started {");
t1.Start();
t2.Start();
Console.WriteLine("Main t1 t2 started }"); Console.WriteLine("Main wait t1 t2 end {");
Task.WaitAll(t1, t2);
Console.WriteLine("Main wait t1 t2 end }"); Console.WriteLine("Sample 4-3 Main }"); Console.ReadKey();
}
}
} BlockingCollection
一个支持界限和阻塞的容器 Add :向容器中插入元素
TryTake:从容器中取出元素并删除
TryPeek:从容器中取出元素,但不删除。
CompleteAdding:告诉容器,添加元素完成。此时如果还想继续添加会发生异常。
IsCompleted:告诉消费线程,生产者线程还在继续运行中,任务还未完成。 示例程序: 程序中,消费者线程完全使用 while (!_TestBCollection.IsCompleted) 作为退出运行的判断条件。
在Worker1中,有两条语句被注释掉了,当i 为50时设置CompleteAdding,但当继续向其中插入元素时,系统抛出异常,提示无法再继续插入。 using System;
using System.Text; using System.Threading.Tasks;
using System.Collections.Concurrent; namespace Sample4_4_concurrent_bag
{
class Program
{
internal static BlockingCollection<int> _TestBCollection; class ThreadWork1 // producer
{
public ThreadWork1()
{ } public void run()
{
System.Console.WriteLine("ThreadWork1 run { ");
for (int i = 0; i < 100; i++)
{
System.Console.WriteLine("ThreadWork1 producer: " + i);
_TestBCollection.Add(i);
//if (i == 50)
// _TestBCollection.CompleteAdding();
}
_TestBCollection.CompleteAdding(); System.Console.WriteLine("ThreadWork1 run } ");
}
} class ThreadWork2 // consumer
{
public ThreadWork2()
{ } public void run()
{
int i = 0;
int nCnt = 0;
bool IsDequeuue = false;
System.Console.WriteLine("ThreadWork2 run { ");
while (!_TestBCollection.IsCompleted)
{
IsDequeuue = _TestBCollection.TryTake(out i);
if (IsDequeuue)
{
System.Console.WriteLine("ThreadWork2 consumer: " + i * i + " =====" + i);
nCnt++;
}
}
System.Console.WriteLine("ThreadWork2 run } ");
}
} static void StartT1()
{
ThreadWork1 work1 = new ThreadWork1();
work1.run();
} static void StartT2()
{
ThreadWork2 work2 = new ThreadWork2();
work2.run();
}
static void Main(string[] args)
{
Task t1 = new Task(() => StartT1());
Task t2 = new Task(() => StartT2()); _TestBCollection = new BlockingCollection<int>(); Console.WriteLine("Sample 4-4 Main {"); Console.WriteLine("Main t1 t2 started {");
t1.Start();
t2.Start();
Console.WriteLine("Main t1 t2 started }"); Console.WriteLine("Main wait t1 t2 end {");
Task.WaitAll(t1, t2);
Console.WriteLine("Main wait t1 t2 end }"); Console.WriteLine("Sample 4-4 Main }"); Console.ReadKey();
}
}
} 当然可以尝试在Work1中注释掉 CompleteAdding 语句,此时Work2陷入循环无法退出。 ConcurrentDictionary
对于读操作是完全无锁的,当很多线程要修改数据时,它会使用细粒度的锁。 AddOrUpdate:如果键不存在,方法会在容器中添加新的键和值,如果存在,则更新现有的键和值。
GetOrAdd:如果键不存在,方法会向容器中添加新的键和值,如果存在则返回现有的值,并不添加新值。
TryAdd:尝试在容器中添加新的键和值。
TryGetValue:尝试根据指定的键获得值。
TryRemove:尝试删除指定的键。
TryUpdate:有条件的更新当前键所对应的值。
GetEnumerator:返回一个能够遍历整个容器的枚举器。 程序示例: using System;
using System.Text; using System.Threading.Tasks;
using System.Collections.Concurrent; namespace Sample4_5_concurrent_dictionary
{
class Program
{
internal static ConcurrentDictionary<int, int> _TestDictionary; class ThreadWork1 // producer
{
public ThreadWork1()
{ } public void run()
{
System.Console.WriteLine("ThreadWork1 run { ");
for (int i = 0; i < 100; i++)
{
System.Console.WriteLine("ThreadWork1 producer: " + i);
_TestDictionary.TryAdd(i, i);
} System.Console.WriteLine("ThreadWork1 run } ");
}
} class ThreadWork2 // consumer
{
public ThreadWork2()
{ } public void run()
{
int i = 0, nCnt = 0;
int nValue = 0;
bool IsOk = false;
System.Console.WriteLine("ThreadWork2 run { ");
while (nCnt < 100)
{
IsOk = _TestDictionary.TryGetValue(i, out nValue);
if (IsOk)
{
System.Console.WriteLine("ThreadWork2 consumer: " + i * i + " =====" + i);
nValue = nValue * nValue;
_TestDictionary.AddOrUpdate(i, nValue, (key, value) => { return value = nValue; });
nCnt++;
i++;
}
}
System.Console.WriteLine("ThreadWork2 run } ");
}
} static void StartT1()
{
ThreadWork1 work1 = new ThreadWork1();
work1.run();
} static void StartT2()
{
ThreadWork2 work2 = new ThreadWork2();
work2.run();
}
static void Main(string[] args)
{
Task t1 = new Task(() => StartT1());
Task t2 = new Task(() => StartT2());
bool bIsNext = true;
int nValue = 0; _TestDictionary = new ConcurrentDictionary<int, int>(); Console.WriteLine("Sample 4-5 Main {"); Console.WriteLine("Main t1 t2 started {");
t1.Start();
t2.Start();
Console.WriteLine("Main t1 t2 started }"); Console.WriteLine("Main wait t1 t2 end {");
Task.WaitAll(t1, t2);
Console.WriteLine("Main wait t1 t2 end }"); foreach (var pair in _TestDictionary)
{
Console.WriteLine(pair.Key + " : " + pair.Value);
} System.Collections.Generic.IEnumerator<System.Collections.Generic.KeyValuePair<int, int>>
enumer = _TestDictionary.GetEnumerator(); while (bIsNext)
{
bIsNext = enumer.MoveNext();
Console.WriteLine("Key: " + enumer.Current.Key +
" Value: " + enumer.Current.Value); _TestDictionary.TryRemove(enumer.Current.Key, out nValue);
} Console.WriteLine("\n\nDictionary Count: " + _TestDictionary.Count); Console.WriteLine("Sample 4-5 Main }"); Console.ReadKey();
}
}
}
 

【C#】58. .Net中的并发集合——BlockingCollection的更多相关文章

  1. .Net多线程编程—并发集合

    并发集合 1 为什么使用并发集合? 原因主要有以下几点: System.Collections和System.Collections.Generic名称空间中所提供的经典列表.集合和数组都不是线程安全 ...

  2. C#并行编程-并发集合

    菜鸟学习并行编程,参考<C#并行编程高级教程.PDF>,如有错误,欢迎指正. 目录 C#并行编程-相关概念 C#并行编程-Parallel C#并行编程-Task C#并行编程-并发集合 ...

  3. 转载 .Net多线程编程—并发集合 https://www.cnblogs.com/hdwgxz/p/6258014.html

    集合 1 为什么使用并发集合? 原因主要有以下几点: System.Collections和System.Collections.Generic名称空间中所提供的经典列表.集合和数组都不是线程安全的, ...

  4. C#并发集合(转)

    出处:https://www.cnblogs.com/Leo_wl/p/6262749.html?utm_source=itdadao&utm_medium=referral 并发集合 1 为 ...

  5. C#并发集合

    并发集合   并发集合 1 为什么使用并发集合? 原因主要有以下几点: System.Collections和System.Collections.Generic名称空间中所提供的经典列表.集合和数组 ...

  6. java多线程中并发集合和同步集合有哪些?区别是什么?

    java多线程中并发集合和同步集合有哪些? hashmap 是非同步的,故在多线程中是线程不安全的,不过也可以使用 同步类来进行包装: 包装类Collections.synchronizedMap() ...

  7. .NET中的并发操作集合

    更新记录 本文迁移自Panda666原博客,原发布时间:2021年7月1日. 一.并发集合 .NET中提供了相当多线程安全的集合,它们都在System.Collections.Concurrent命名 ...

  8. JAVA并发七(多线程环境中安全使用集合API)

    在集合API中,最初设计的Vector和Hashtable是多线程安全的.例如:对于Vector来说,用来添加和删除元素的方法是同步的.如果只有一个线程与Vector的实例交互,那么,要求获取和释放对 ...

  9. 转:【Java并发编程】之八:多线程环境中安全使用集合API(含代码)

    转载请注明出处:http://blog.csdn.net/ns_code/article/details/17200509     在集合API中,最初设计的Vector和Hashtable是多线程安 ...

随机推荐

  1. Mysql 主从一致校验工具------Maatkit工具包

    Maatkit工具包 http://www.maatkit.org/ 简介 maatkit是一个开源的工具包,为mysql日常管理提供了帮助.目前,已被Percona公司收购并维护.其中: mk-ta ...

  2. pyecharts的使用

    折线图1 import pyecharts.options as opts from pyecharts.charts import Line ​ x_data = ["Mon", ...

  3. java static学习

    原创,转载请注明来源sogeisetsu的博客园 static,在类里面定义公共的属性,它可以统一修改,并只占一个内存.从而达到方便修改和少占内存的目的 先放上代码,您可以先越过代码,看后面的讲解内容 ...

  4. Bootstrap 提示工具(Tooltip)插件

    一.提示工具(Tooltip)插件根据需求生成内容和标记,默认情况下是把提示工具(tooltip)放在它们的触发元素后面. 有以下两种方式添加提示工具(tooltip): 1.通过data属性:如需添 ...

  5. Leetcode周赛165

    目录 找出井字棋的获胜者 思路 代码 不浪费原料的汉堡制作方案 思路 代码 统计全为 1 的正方形子矩阵 思路 代码 分割回文串 III 思路 代码 找出井字棋的获胜者 思路 模拟. 代码 class ...

  6. Kdevelop的C++断点调试设置

    1. CMakeLists.txt 需要设置为 Debug 模式 示例 cmake_minimum_required(VERSION 2.8) Project (Eigen_test) include ...

  7. JavaScript词法作用域—你不知道的JavaScript上卷读书笔记(一)

    前段时间在每天往返的地铁上抽空将 <你不知道的JavaScript(上卷)>读了一遍,这本书很多部分写的很是精妙,对于接触前端时间不太久的人来说,就好像是叩开了JavaScript的另一扇 ...

  8. ABP 网站发布

    报错1:HTTP Error 503. The service is unavailable. 解决:IIS->应用程序池->高级设置->进程模型->标识.将内置账户更改为Ne ...

  9. Debian9下安装Python3 pip

    Debian9下安装Python3 pip 使用apt-get安装Python3-pip包 apt-get install python3-pip

  10. [PHP] PHP汉字转拼音的方法

    PHP汉字转拼音的方法 代码如下: <?php // 此类是依据ASCII码转换,GB2312库对多音字也无能为力. // GB2312标准共收录6763个汉字,不在范围内的汉字是无法转换,如: ...