[.NET]ConcurrentDictionary 线程安全的集合类

  ConcurrentDictionary 是.NET 4.0中新添加的,相信其性能是比自己手动对Dictionary加锁要好得多

   其中大部分方法是保证线程安全的:

   TryAdd()

TryUpdate()

TryRemove()

AddOrUpdate()

GetOrAdd()



其中有些地方要注意的:

1.作为GetOrAdd(TKey key, Func<TKey, TValue> valueFactory)参数的委托 ,不保证里面代码的线程安全,也不保证委托执行的次数

当要获取的key的value不存在的时候,就会执行委托返回一个新的value值并添加都集合里面去

  但是,由于委托并不在ConcurrentDictionary 的锁里面,所以委托里面的代码不是线程安全的,而且不保证委托只被执行一次。

所以要保证委托总是返回确定的值,或者用Lazy<T>代替委托

 

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Collections.Concurrent;
using System.Threading.Tasks;

namespace ConsoleApplication2
{
class Program
{
static ManualResetEvent Signal = new ManualResetEvent(false);//用于同步线程
static int Count = 0;
static ConcurrentDictionary<int, int> MyDictionary = new ConcurrentDictionary<int, int>();
public static void TaskAction()
{
Signal.WaitOne();


MyDictionary.GetOrAdd(1, key =>
{

Interlocked.Increment(ref Count);//以原子操作的形式递增指定变量的值并存储结果
Console.WriteLine(Thread.CurrentThread.ManagedThreadId.ToString());
return 1;
});
}
delegate void myDel();
static void Main(string[] args)
{
myDel del = TaskAction;




List<IAsyncResult> results = new List<IAsyncResult>();

for (int i = 0; i < 4; i++)
{
results.Add(del.BeginInvoke(null, null));
}
Thread.Sleep(3000);//留足够的时间让线程池准备好线程
Signal.Set();

results.ForEach(item => item.AsyncWaitHandle.WaitOne());




Console.WriteLine("执行次数" + Count.ToString());

Console.ReadKey();
}

}
}


上面代码执行的结果可能与原本的结果1是不一致的。

如果采用.NET4.0的Task来试验的话,要同时开几十个线程才偶尔出现错误值,可见Task的优化程度。

 

 

2.关于ConcurrentDictionary 的迭代

如果以下面的形式进行迭代,迭代的时候不会阻塞线程,但是会读到脏数据

           foreach (var item in MyDictionary)
{
//其他代码
}

如果下面这种形式,迭代的结果将是MyDictionary某一时刻的快照,不包含脏数据

           foreach (var item in MyDictionary.ToArray())
{
//其他代码
}

同时,ConcurrentDictionary 的Count, Keys, Values 属性也是某一时刻的快照

 

3.对ConcurrentDictionary 的某个Value的引用的读写不是线程安全的

下面执行的结果不是可预料的:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Collections.Concurrent;
namespace ConsoleApplication2
{
class Program
{
public class myclass
{
public int value = 0;
}
public static ConcurrentDictionary<int, myclass> MyDictionary = new ConcurrentDictionary<int, myclass>();

static void Main(string[] args)
{

myclass myclass = new Program.myclass();
myclass.value = 0;
MyDictionary.TryAdd(1, myclass);
Thread xxx = new Thread(() =>
{
for (int i = 0; i < 100000000; i++)
{
myclass value;
if (MyDictionary.TryGetValue(1, out value))
{
value.value++;

}


}
});
Thread vvv = new Thread(() =>
{
for (int i = 0; i < 100000000; i++)
{

myclass value;
if (MyDictionary.TryGetValue(1, out value))
{
value.value--;
}
}
});
xxx.Start();
vvv.Start();

xxx.Join();
vvv.Join();
Console.WriteLine(myclass.value);
}
}
}







   

posted @ 2012-02-16 23:23  杂草  阅读(2045)  评论(1编辑  收藏  举报