问题
I am trying to spawn threads in a for each loop using a copy of the value in a dict.
My initial understanding was that the foreach
would create a new scope, and led to:
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));
}
which writes
1
2
2
2
instead of (expected):
1
2
1
2
So I tried cloning the kvp in the foreach:
KeyValuePair<string, string> tmp = new KeyValuePair<string, string>(record.Key, record.Value);
but that renders the same result.
I've also tried it with System.Parallel.ForEach
but that seems need values that are not dynamic, which is a bit of a train smash for my dictionary.
How can I iterate through my Dictionary with threads?
回答1:
The problem is closure over your lambda, the way to fix is is to add a local variable inside the for loop
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));
}
What is happening in your version is it captures the variable record
not the value of the variable record. So when the timer runs the 2nd time it uses the "current value" of record
which is the 2nd element in the array.
Behind the scenes this is what is kinda what is happening in your version of the code.
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);
}
See how when your function ran it for the first itteration had the correct version of _recordStored
, but after _recordStored
got overwritten it will only show the last set value. By creating a local variable it does not do that overwriting.
A way to imagine it (I am not sure how I could represent it in a code example) is it creates _recordStored1
the first loop, _recordStored2
the 2nd loop, and so on. The function uses the correct version of _recordStored#
for when it calls the the function.
来源:https://stackoverflow.com/questions/17658162/how-do-i-iterate-through-a-dynamically-valued-dictionary-in-parallel