【问题标题】:How to avoid captured variables?如何避免捕获的变量?
【发布时间】:2011-04-19 13:33:11
【问题描述】:

我遇到了问题

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

        ThreadPool.QueueUserWorkItem(waitCallback);
    }
}

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

【问题讨论】:

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


【解决方案1】:

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

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);
    }
}

【讨论】:

  • 非常感谢。一直在这个问题上挠头大约一个小时。所以每个循环都会实例化一个新的局部变量,每个变量都是独立于其他变量捕获的——明白了。
【解决方案2】:

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);
}

这简单易懂。它说明了开发人员的意图,而不会用额外的变量使代码混乱(就像解决此问题的默认方式一样)。

【讨论】:

  • @mquander:可能是口味问题,但原理基本相同。在函数的情况下,复制是通过函数参数隐式进行的。
【解决方案3】:

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

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);
    }
}

【讨论】:

  • 是的,您也可以使用 Tuple&lt;string, string&gt; 代替 KeyValuePair。
  • @Henk 需要 .NET 4.0
  • 我现在认为是默认值:除非另有说明。
【解决方案4】:

我会这样写整个事情,这样可以避开问题,并且绝对不会对正在发生的事情产生任何疑问:

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-10-17
    • 1970-01-01
    • 2017-10-24
    • 1970-01-01
    • 2017-05-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多