为什么异步时我会得到不同的输出?

时间:2014-10-21 15:18:12

标签: .net multithreading visual-studio-2012

我将以下代码写入文本框:

for (int ii = 0; ii < 5; ii++)
{
    textBox1.Text += String.Format("Writing Line {0}{1}", ii + 1, System.Environment.NewLine);
}

我关闭了预期的输出:

Writing Line 1
Writing Line 2
Writing Line 3
Writing Line 4
Writing Line 5

但是,当我更新asyncornusly或来自此处的其他线程时:How to update the GUI from another thread in C#?

for (int ii = 0; ii < 5; ii++)
{
    textBox1.BeginInvoke(new Action(() => textBox1.Text += String.Format("Writing Line {0}{1}", ii + 1 , System.Environment.NewLine)));
}

我得到了

Writing Line 5
Writing Line 5
Writing Line 5
Writing Line 5
Writing Line 5

最后一行打印5次,我希望输出与单线程同步输出匹配?

我需要更改什么才能匹配第二个线程的第一个输出?

2 个答案:

答案 0 :(得分:2)

这是因为您在异步操作中引用了循环变量ii。调用操作时,循环已经完成,ii的值为4.这就是为什么你在异步版本中得到所有五个(即4 + 1)。

ii制作一个临时工具可以解决问题:

for (int ii = 0; ii < 5; ii++) {
    var iii = ii;
    textBox1.BeginInvoke(new Action(() => textBox1.Text += String.Format("Writing Line {0}{1}", iii + 1 , System.Environment.NewLine)));
}

此错误是由访问修改后的闭包引起的。有关详细信息,请参阅this Q&A

答案 1 :(得分:0)

变量ii被视为在循环外定义的begin。它的编译就像它看起来像这样:

int ii;
for (ii = 0; ii < 5; ii++)
{
    textBox1.Text += String.Format("Writing Line {0}{1}", ii + 1, System.Environment.NewLine);
}

因此,您的lambda正在捕获共享变量,而不是值的唯一实例。你需要在循环中制作一个副本:

for (int ii = 0; ii < 5; ii++)
{
    int localCopy = ii;

    textBox1.Text += String.Format("Writing Line {0}{1}", localCopy  + 1, System.Environment.NewLine);
}

这也是foreach语句的问题。在C#5中,他们改变了foreach的行为,以便将变量视为在foreach的主体内声明,因此捕获将正常工作。因此你可以写:

foreach(int ii in Enumerable.Range(0,5))
{
    textBox1.BeginInvoke(new Action(() => textBox1.Text += String.Format("Writing Line {0}{1}", ii + 1 , System.Environment.NewLine)));
}

这里重点是forforeach在捕获方式上的表现不同。使用for时,变量被视为在循环之外,foreach认为变量在(因此可以安全捕获)内。