这篇是并发集合中的最后一篇,介绍一下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. Linux shell if条件判断2

    前面介绍linux shell的if判断的语法,现在再补充一点. Linux shell if条件判断1 分支判断结构     if , case   下面两个结构语法,已经在前面有过示例. 结构1: ...

  2. TF-IDF算法介绍及实现

    目录 1.TF-IDF算法介绍 (1)TF是词频(Term Frequency) (2) IDF是逆向文件频率(Inverse Document Frequency) (3)TF-IDF实际上是:TF ...

  3. 04-cmake语法-STREQUAL

    STREQUAL 用于比较字符串,相同返回 true .

  4. 配置本地 yum 仓库

    配置本地 yum 仓库 # yum 官网 http://yum.baseurl.org/ # yum 手册页 man yum man yum.conf SEE ALSO pkcon (1) yum.c ...

  5. ActiveMQ消息可靠性-签收

    非事务模式下消费者签收 动签收就像快递到达时,快递寄送点给你签收了,不用你自己去签收,而手动签收就是必须我本人签收, 自动签收(默认为自动签收) 手动签收:能够避免消息的重复消费 当设置为手动签收时, ...

  6. eclipse export runnable jar(导出可执行jar包) runnable jar可以执行的

    如果要导出可运行的JAR文件,需要选择Runnable Jar File. 1. 选择要到处JAR文件的工程,右键选择“Export”: 2. 选择“Java-->Runnable JAR fi ...

  7. #pragma once用法总结

    1.#pragmaonce这个宏有什么作用? 为了避免同一个头文件被包含(include)多次,C/C++中有两种宏实现方式:一种是#ifndef方式:另一种是#pragma once方式. 在能够支 ...

  8. Browser cannot find PAC because wpad hostname cannot be resolved

    Enterprise Network administrator may faultly forget to configure wpad hostname to DNS server. If use ...

  9. 把antd的组件源码搬到Ant Design Pro中使用

    把组件源码搬过来后,样式死活不生效,经过1天的努力,有说less-loader的,有说webpack配置,还有说babel配置的,最后,我自己找到了方法 就是在global.less中使用@impor ...

  10. 使用css怎么让谷歌支持小于12px的文字比如10px

    1.小于12px的字体,如果内容固定,可以将内容切除做图片,没有兼容问题. 2.-webkit-text-size-adjust:none;老版本谷歌,27版本之后无用 3.-webkit-trans ...