如何避免捕获变量?

时间:2011-04-19 13:33:12

标签: c# .net concurrency delegates captured-variable

我遇到了

的问题
foreach(var category in categories)
{
    foreach(var word in words)
    {
        var waitCallback = new WaitCallback(state =>
        {
            DoSomething(word, category);
        });

        ThreadPool.QueueUserWorkItem(waitCallback);
    }
}

执行DoSomething时,它会接收每个捕获变量的最新值,而不是我想要的值。我可以想象一个解决方案,但它想象你们可以提出更好的解决方案

4 个答案:

答案 0 :(得分:13)

解决此问题的规范方法是将值复制到临时变量中,这些变量在循环中声明为

foreach(var category in categories)
{
    var catCopy = category;
    foreach(var word in words)
    {
        var wordCopy = word;
        var waitCallback = new WaitCallback(state =>
        {
            DoSomething(wordCopy, catCopy);
        });

        ThreadPool.QueueUserWorkItem(waitCallback);
    }
}

答案 1 :(得分:4)

Refactor来:

foreach(var category in categories) {
  foreach(var word in words) {
    DoSomethingAsync(word, category);
  }
}

...

private void DoSomethingAsync(string word, string category) {
  var waitCallback = new WaitCallback(state => DoSomething(word, category));
  ThreadPool.QueueUserWorkItem(waitCallback);
}

这很简单易懂。它说明了开发人员的 intent ,而没有使用额外的变量使代码混乱(如解决此问题的默认方式)。

答案 2 :(得分:1)

作为参考,我想以下可以解决我的问题:

foreach(var category in categories)
{
    foreach(var word in words)
    {
        var waitCallback = new WaitCallback(state =>
        {
            var kv = (KeyValuePair<string, string>)state;
            DoSomething(kv.Key, kv.Value);
        });

        var state2 = new KeyValuePair<string, string>(word, category);
        ThreadPool.QueueUserWorkItem(waitCallback, state2);
    }
}

答案 3 :(得分:1)

我会写出这样的全部内容,它会避开问题并且完全没有问题:

var callbacks = words.SelectMany(w => categories.Select(c =>
    new WaitCallback(state => {
        DoSomething(w, c);
    })
));

foreach (var callback in callbacks)
    ThreadPool.QueueUserWorkItem(callback);