.NET 4.0 多任务编程 之二 线程安全的集合

随着多核计算机的普及,并行编程技术,也就是多核编程技术也逐渐称为开发的主流。为此,在.NET 4 中就引入了“并行编程”。在.NET 4 中一些列的Library和类为并行编程提供了支持,如:Task Parallel Library,Parallel LINQ等。

在.NET 1.0并行编程技术主要依赖于多线线程技术。多线程最大的问题就是难于使用和管理。在使用多线程的使用,因为它的复杂性,往往使用我们把注意力分散了多线程上。而致使我们的最初目的被掩盖了,比如下面这个例子:

void Add(String x)
{
_collection.Add(x);
}
void List()
{
foreach(int val in _collection)
//do something
}


假如我们使用两个线程,一个进行添加操作,另外一个则进行简单的读操作,很大可能性会抛出一个InvalidOperationException, 因为系统发现集合在读取的同时被修改了。传统的应对方法,就是手工对要操作的对象加锁。即我们可以在对_collection集合进行操作之前对其添加代码lock(_collection)。

然而这种方式毕竟不够简洁,并且在更复杂的情况下它可能会显得非常繁琐,而且容易出错。这时候支持并行操作的集合应运而生了。

[b]Concurrent集合[/b]
和Java集合设计的一团混乱相比,.NET将线程安全的集合单独放在System.Collections.Concurrent这个名称空间里,这些类分别存在于两个不同的dll中,其中在System.dll中有
[list]
[*]BlockingCollection<T>
[*]ConcurrentBag<T>
[/list]
在mscorlib.dll中则稍多一些,他们分别是
[list]
[*]ConcurrentQueue<T>
[*]ConcurrentStack<T>
[*]ConcurrentDictionary<TKey, TValue>
[/list]
首先看看mscorlib中的几个类,这些类和.NET 2.0中普通版本相比,功能基本不变,因此你仍然可以像使用普通的哈希表,队列和堆栈来使用它们。唯一区别比较大的也是在Concurrent命名空间中很常见的各种try函数操作。
在System.dll中的并行集合,这里面的两个类在以前的.NET中是没有的。
首先看看ConcurrentBag<T>,顾名思义,这个类提供并行数据包的功能,这个类相对来说构造比较简单,它继承自四个接口:
[list]
[*]IProducerConsumerCollection<T>
[*]IEnumerable<T>
[*]ICollection
[*]IEnumerable
[/list]
IProducerConsumerCollection<T> 是个新的接口,这个接口提供了生产者/消费者的集合操作,它提供了四个基本的方法:CopyTo(T[],int), ToArray(), TryAdd(T), TryTake(out T)。其中我们主要关注后面的两个方法,TryAdd是添加元素操作,而TryTake则是取元素操作。
不过在ConcurrentBag中,TryAdd方法被设置为protected,外部对象需要通过Add操作来添加元素。另外,取元素的话,除了TryTake之外,我们还可以通过TryPeek来取集合当前的最后一个元素而不删除它。上面的例子经过简单修改之后,就可以避免线程冲突的错误
[code]
static void main(){
ConcurrentBag<string> bag = new ConcurrentBag<string>();
Task.Factory.StartNew(() =>
{
for (int i = 0; i < 1000; i++)
{
bag.Add(i.ToString());
}
bag.Add("Last");
});
Task.Factory.StartNew(() =>
{
foreach (string item in bag)
{
Console.WriteLine(item);
}
}).Wait();}
[/code]
BlockingCollection<T>相对来说,要稍微复杂一些,它也提供了比ConcurrentBag更加丰富的功能。如CompleteAdding和超时设置的TryAdd和TryTake方法等等。

[b]新特性[/b]
并行类同时还提供了一些更丰富的功能,由于Lamda表达式的引入,现在在ConcurrentDictionary<TKey, TValue>你可以通过AddOrUpdate或GetOrAdd添加自己的值生成方案。这使得我们在生成键值对的时候更加方便和简单了。如:

ConcurrentDictionary<int, string> td = new ConcurrentDictionary<int, string>();
Func<int, string> genVar = (i) => i.ToString();
Task.Factory.StartNew(() =>{
for (int i = 0; i < 1000; i++) {
td.GetOrAdd(i, genVar); }});

Task.Factory.StartNew(() =>{
Func<int, string, string> updateVar = (key, oldVar) => oldVar + key;
td.AddOrUpdate(0, genVar, updateVar);
Console.WriteLine(td[0]);}).Wait();


[b]锁[/b]

如果读取共享状态比写入该状态花的时间要长得多(这是常有的情况),读/写锁就被广泛使用以提高可扩展性。


参考:
http://www.bluebytesoftware.com/blog/2009/01/30/ASinglewordReaderwriterSpinLock.aspx
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值