C# 并行编程 之 并发集合 (.Net Framework 4.0)

此文为个人学习《C#并行编程高级教程》的笔记,总结并调试了一些文章中的代码示例。 在以后开发过程中可以加以运用。

对于并行任务,与其相关紧密的就是对一些共享资源,数据结构的并行访问。经常要做的就是对一些队列进行加锁-解锁,然后执行类似插入,删除等等互斥操作。 .NetFramework 4.0 中提供了一些封装好的支持并行操作数据容器,可以减少并行编程的复杂程度。

基本信息

.NetFramework中并行集合的名字空间: System.Collections.Concurrent

并行容器:

这些集合在某种程度上使用了无锁技术(CAS Compare-and-Swap和内存屏障 Memory Barrier),与加互斥锁相比获得了性能的提升。但在串行程序中,最好不用这些集合,它们必然会影响性能。

关于CAS:

关于内存屏障

用法与示例ConcurrentQueue

其完全无锁,但当CAS面临资源竞争失败时可能会陷入自旋并重试操作。

程序示例:

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面临资源竞争失败时可能会陷入自旋并重试操作。

程序示例:

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: 0ThreadWork2 run {ThreadWork1 producer: 1ThreadWork1 producer: 2ThreadWork1 producer: 3ThreadWork1 producer: 4ThreadWork1 producer: 5ThreadWork1 producer: 6ThreadWork1 producer: 7ThreadWork1 producer: 8ThreadWork1 producer: 9ThreadWork1 producer: 10ThreadWork1 producer: 11ThreadWork1 producer: 12ThreadWork1 producer: 13ThreadWork1 producer: 14ThreadWork1 producer: 15ThreadWork1 producer: 16ThreadWork1 producer: 17ThreadWork1 producer: 18ThreadWork1 producer: 19ThreadWork1 producer: 20ThreadWork1 producer: 21ThreadWork1 producer: 22ThreadWork1 producer: 23ThreadWork1 producer: 24ThreadWork1 producer: 25ThreadWork2 consumer: 16 =====4ThreadWork2 consumer: 625 =====25ThreadWork2 consumer: 576 =====24ThreadWork2 consumer: 529 =====23ThreadWork1 producer: 26ThreadWork1 producer: 27ThreadWork1 producer: 28

ConcurrentBag

一个无序的集合,程序可以向其中插入元素,或删除元素。

在同一个线程中向集合插入,删除元素的效率很高。

Add:向集合中插入元素TryTake:从集合中取出元素并删除TryPeek:从集合中取出元素,但不删除该元素。

程序示例:

拥有一颗比九万五千公里还辽阔的心,

C# 并行编程 之 并发集合 (.Net Framework 4.0)

相关文章:

你感兴趣的文章:

标签云: