我正在尝试使用dict中的值副本为每个循环生成线程。
我最初的理解是foreach
会创建一个新的范围,并导致:
Dictionary<string, string> Dict = new Dictionary<string, string>() { { "sr1", "1" }, { "sr2", "2" } };
foreach (KeyValuePair<string, string> record in Dict) {
new System.Threading.Timer(_ =>
{
Console.WriteLine(record.Value);
}, null, TimeSpan.Zero, new TimeSpan(0, 0, 5));
}
写的
1
2
2
2
而不是(预期):
1
2
1
2
所以我尝试在foreach中克隆kvp:
KeyValuePair<string, string> tmp = new KeyValuePair<string, string>(record.Key, record.Value);
但是会产生相同的结果。
我也尝试过System.Parallel.ForEach
,但这似乎需要非动态的值,这对我的字典来说有点像火车一样。
如何使用线程迭代我的词典?
答案 0 :(得分:7)
问题是closure over your lambda,修复方法是在for循环中添加局部变量
Dictionary<string, string> Dict = new Dictionary<string, string>() { { "sr1", "1" }, { "sr2", "2" } };
foreach (KeyValuePair<string, string> record in Dict) {
var localRecord = record;
new System.Threading.Timer(_ =>
{
Console.WriteLine(localRecord.Value);
}, null, TimeSpan.Zero, new TimeSpan(0, 0, 5));
}
您的版本中发生的是它捕获变量record
而不是变量记录的值。因此,当计时器第二次运行时,它使用record
的“当前值”,这是数组中的第二个元素。
在幕后,这就是您的代码版本中发生的事情。
public void MainFunc()
{
Dictionary<string, string> Dict = new Dictionary<string, string>() { { "sr1", "1" }, { "sr2", "2" } };
foreach (KeyValuePair<string, string> record in Dict) {
_recordStored = record;
new System.Threading.Timer(AnnonFunc, null, TimeSpan.Zero, new TimeSpan(0, 0, 5));
}
}
private KeyValuePair<string, string> _recordStored;
private void AnnonFunc()
{
Console.WriteLine(_recordStored.Value);
}
查看函数在第一次运行时如何运行_recordStored
的正确版本,但在_recordStored
被覆盖后,它将仅显示最后一个设置值。通过创建局部变量,它不会覆盖。
一种想象它的方式(我不确定如何在代码示例中表示它)是它创建_recordStored1
第一个循环,_recordStored2
第二个循环,依此类推。该函数在调用函数时使用正确版本的_recordStored#
。