C# 带有委托的QueueUserWorkItem不起作用,但WaitCallBack起作用

C# 带有委托的QueueUserWorkItem不起作用,但WaitCallBack起作用,c#,multithreading,delegates,C#,Multithreading,Delegates,在下面的问题中,我发现了一个以类型安全的方式调用QueueUserWorkItem的巧妙技巧,即传递一个委托,而不是WaitCallBack和一个对象。然而,它并不像人们所期望的那样工作 下面是一些示例代码和输出,演示了这个问题 for (int i = 0; i < 10; ++i) { // doesn't work - somehow DoWork is invoked with i=10 each time!!! ThreadPool.QueueUserWorkI

在下面的问题中,我发现了一个以类型安全的方式调用QueueUserWorkItem的巧妙技巧,即传递一个委托,而不是WaitCallBack和一个对象。然而,它并不像人们所期望的那样工作

下面是一些示例代码和输出,演示了这个问题

for (int i = 0; i < 10; ++i)
{
    // doesn't work - somehow DoWork is invoked with i=10 each time!!!
    ThreadPool.QueueUserWorkItem(delegate { DoWork("closure", i); });

    // not type safe, but it works
    ThreadPool.QueueUserWorkItem(new WaitCallback(DoWork), Tuple.Create("    WCB", i));
}

void DoWork(string s, int i)
{
    Console.WriteLine("{0} - i:{1}", s, i);
}

void DoWork(object state)
{
    var t = (Tuple<string, int>)state;
    DoWork(t.Item1, t.Item2);
}
请注意,当使用闭包调用QueueUserWorkitem时,每次调用i=10,但当使用WaitCallBack时,您会得到正确的值0-9

因此,我的问题是:

  • 为什么在使用闭包/委托方式时传递的值不正确
  • 我到底怎么能活到10岁?在循环中,它只有0-9的值,对吗

  • 这两个问题的答案都与创建匿名方法时闭包的范围有关

    执行此操作时:

    // Closure for anonymous function call begins here.
    for (int i = 0; i < 10; ++i)
    {
        // i is captured
        ThreadPool.QueueUserWorkItem(delegate { DoWork("closure", i); });
    }
    
    在这里,闭包并不扩展到循环之外,而是扩展到循环内部的值

    也就是说,对的第二次调用将生成所需的结果,因为您在代理排队时创建了,该值在该点是固定的

    请注意(但不是像您使用的那样针对)

    如果您想利用这一事实,可以在上调用以使用
    foreach

    foreach (int i in Enumerable.Range(0, 10))
    {
        // Closure for anonymous function call begins here.
        ThreadPool.QueueUserWorkItem(delegate { DoWork("closure", i); });
    }
    

    这是因为变量是如何捕获的:委托将在实际执行时获取
    i
    的值,而不是在声明时,因此到那时它们都是10。尝试复制到局部变量:

    for (int i = 0; i < 10; ++i)
    {
        int j = i;        
        ThreadPool.QueueUserWorkItem(delegate { DoWork("closure", j); });
    
    for(int i=0;i<10;++i)
    {
    int j=i;
    QueueUserWorkItem(委托{DoWork(“closure”,j);});
    
    Loop variable capture,再次。在堆栈溢出中搜索该术语。@usr或,您可以找到其中一个重复项并投票关闭。这将是更有效的做法。我明白了。谢谢!如果我在不知道的情况下看到该代码,我会想“wtf-为什么要复制?”
    foreach (int i in Enumerable.Range(0, 10))
    {
        // Closure for anonymous function call begins here.
        ThreadPool.QueueUserWorkItem(delegate { DoWork("closure", i); });
    }
    
    for (int i = 0; i < 10; ++i)
    {
        int j = i;        
        ThreadPool.QueueUserWorkItem(delegate { DoWork("closure", j); });