从本节开始,我们将要重点介绍如何解决并行运算中的哪两个问题。竞态问题其实就是一个共享数据的问题,所以许多资料也干脆把这个问题称为Share Data. 在多线程环境中我们知道很多解决这个问题的方法; .NET Framework 4 提供了System.Collections.Concurrent一个新的命名空间来解决这个问题。
当开发人员在开发并行程序的过程中,需要在多个并行Task之间共享一个集合时,这个命名空间提供的Concurrent Collections是最佳选择;它们是线程安全的同时还提供了一个轻量级的同步机制。
这个命名空间提供了以下个基本的并发集合 – ConcurrentBag, ConcurrentDictionary, ConcurrentQueue 、ConcurrentStack和BlockingCollection,OrderablePartitioner<TSource>、Partitioner、Partitioner<TSource>; 还有一个IProducerConsumerCollection<T>接口。前4个在这章中跟大家分享一下,后面几个将在后续文章中跟大家分享。
I. ConcurrentQueue
1: private const int NUM_MAX = 100;
2:
3: static void Main(string[] args)
4: {
5: ConcurrentQueue<Int32> concurrentQueue = new ConcurrentQueue<Int32>();
6:
7: try
8: {
9: Task.Factory.StartNew(() =>
10: {
11: for (int i = 0; i < NUM_MAX; i++)
12: {
13: Console.WriteLine("###################: {0}", i);
14: concurrentQueue.Enqueue(i);
15: }
16: });
17:
18: Task.Factory.StartNew(() => {
19: for (int i = 0; i < NUM_MAX; i++)
20: {
21: int queueElement;
22: bool success = concurrentQueue.TryDequeue(out queueElement);
23: if (success)
24: {
25: Console.WriteLine("-----------------: {0}", queueElement);
26: }
27: }
28: });
29: }
30: catch (System.AggregateException ex)
31: {
32: throw ex;
33: }
34:
35: Console.ReadLine();
36: }
Queue是遵循FIFO的集合,它提供了三个重要的方法
TryPeek() | 获得Queue中的第一个元素,但是并不把它从Queue中移除 |
TryDequeue() | 获得Queue中的第一个元素,并把它从Queue中移除 |
Enqueue() | 把一个元素放到Queue中 |
执行上面的代码可以得到类似如下的结果:
II. ConcurrentStack
Stack是遵循LIFO的集合,它提供了如下一些重要的方法:
Push(T) | 在栈顶插入一个元素 |
PushRange(T[]) | 在栈顶插入多个元素 |
PushRange(T[], int startIndex, int count) | 在栈顶插入多个元素,startIndex是指T[]从零开始的偏移量,从此处开始将元素插入到栈的顶部, count是指插入的数量 |
TryPeek(out T result) | 从栈顶返回一个元素,但是并不把它从栈顶移出 |
TryPop(out T result) | 从栈顶返回一个元素,并不把它从栈顶移除 |
TryPopRange(out T[]) | 将从栈顶部弹出的对象添加到的T[] |
TryPopRange(out T[]) | 将从栈顶部弹出的对象添加到的T[],startIndex是指T[]从零开始的偏移量,从此处开始将元素插入到T[], count是指插入的数量 |
III. ConcurrentBag
ConcurrentBag无序集合
TryPeek(out T result) | 返回一个对象但不移除该对象, |
TryTake( out T result) |
返回一个对象并移除该对象 |
IV. ConcurrentDictionary
键/值对集合
AddOrUpdate(TKey, Func<TKey, TValue>, Func<TKey, TValue, TValue>) | 如果指定的键尚不存在,则将键/值对添加到 ConcurrentDictionary<TKey, TValue> 中;如果指定的键已存在,则更新 ConcurrentDictionary<TKey, TValue> 中的键/值对。 |
AddOrUpdate(TKey, TValue, Func<TKey, TValue, TValue>) | 如果指定的键尚不存在,则将键/值对添加到 ConcurrentDictionary<TKey, TValue> 中;如果指定的键已存在,则更新 ConcurrentDictionary<TKey, TValue> 中的键/值对。 |
GetOrAdd(TKey, Func<TKey, TValue>) | 如果指定的键尚不存在,则将键/值对添加到 ConcurrentDictionary<TKey, TValue> 中。 |
GetOrAdd(TKey, TValue) | 如果指定的键尚不存在,则将键/值对添加到 ConcurrentDictionary<TKey, TValue> 中。 |
TryAdd(TKey, TValue) | 尝试将指定的键和值添加到 ConcurrentDictionary<TKey, TValue> 中。 |
TryRemove(TKey, out TValue) | 尝试将制定的键和值移除,值将会被放到TResult中。如果移除成功,则返回true. |
这个集合中还有一些很有意思的方法,大家可以查看msdn来获得更多的帮助。