【发布时间】:2013-10-15 14:13:10
【问题描述】:
做了一个小例子来选择多线程环境中频繁更新字典的最佳方案。然后观察到“奇怪”的迭代行为。
using System;
using System.Collections.Generic;
using System.Threading;
namespace DictionaryTest
{
class Program
{
static int _n;
static Dictionary<int, string> _dict = new Dictionary<int, string>();
static void Main(string[] args) {
for (int i = 0; i < 50; i++) _dict[i] = "FIRST";
new Thread(ReadDict).Start();
Thread.Sleep(30);
// CASE A, Throws exception AS EXPECTED.
//_dict.Clear();
// CASE B, ReadDict continues iterate on old dictionary values!!!???
//_dict = new Dictionary<int, string>();
// CASE C
UpdateDict();
ReadDict();
Console.ReadKey();
}
private static void ReadDict() {
// Read Method X
// (continues iterate on old dictionary values!!!???)
//
foreach (var kvp in _dict) {
Thread.Sleep(3);
Console.WriteLine("{0,3} {1,4} {2,6} :{3,5} Method X", Interlocked.Increment(ref _n), kvp.Key, kvp.Value, Thread.CurrentThread.ManagedThreadId);
}
// Read Method Y
//
//for (int i = 0; i < 50; i++) {
// Thread.Sleep(3);
// Console.WriteLine("{0,3} {1,4} {2,6} :{3,5} Method Y", Interlocked.Increment(ref _n), i, _dict[i], Thread.CurrentThread.ManagedThreadId);
//}
}
private static void UpdateDict() {
var tmp = new Dictionary<int, string>();
for (int i = 0; i < 50; i++) tmp[i] = "SECOND";
_dict = new Dictionary<int, string>(tmp);
}
}
}
组合:
- CASE A 和(方法 X 或方法 Y) - 按预期抛出异常!
- 案例 B 和方法 X - 继续迭代旧字典值???
- 案例 C 和方法 X - 继续迭代旧字典值???
- 案例 C 和方法 Y - 仅迭代更新的值如预期!
foreach 循环是否会拍摄静态 Dictionary 成员的某种内部快照?如果是这样,CASE A 也应该工作,但它没有。
有人可以解释导致这种奇怪行为的原因吗?
编辑:
尽管使用了 ConcurrentDictionary 和代码锁定,但基本上我的问题是关于字典更新方法之间的差异:将字典的新副本分配为全新对象或更好地迭代某些集合并使用 dict[key]= 单独更新新值我的对象方法?我不需要保留对值对象的引用,只需替换它。
【问题讨论】:
-
简单的答案是,您的代码不是线程安全的。如果你想编写线程安全的代码,你真的应该在 ReadDict 和 UpdateDict 方法内容周围使用一个锁块。否则,行为真的是不可预测的 (msdn.microsoft.com/en-us/library/c5kehkcz.aspx)。
标签: c# .net dictionary iteration