如何捕获Thread中的Right变量

时间:2012-10-05 08:46:38

标签: c# multithreading

为什么会发生这种情况?

你启动一个线程,它运行一个类。

在该类中有一个for循环,在for循环中有一个“写入消息框”和另一个类。

为什么我把写入和类(首先出现的那个)的命令影响了我的数据的准确性?

(这是在线程正在运行的类中)

int atPDFNumber= 0
foreach (var z in q)
        {
                atPDFNumber++;

                convertToImage(z.FullName);

                txtboxtest.BeginInvoke(
            ((Action)(() => txtboxtest.Text += atPDFNumber.ToString())));
}

那个输出给了我一些重叠的值。输出是

运行1:1 2 3 4 5 6 7 8 9 10 11 12 13 14

运行2:1 3 3 4 5 6 7 8 9 10 11 12 13 14

运行2:1 2 3 4 5 6 7 8 9 11 11 12 13 14

如果以正确的顺序运行它们

foreach (var z in q)
            {
                    atPDFNumber++;

                txtboxtest.BeginInvoke(
                ((Action)(() => txtboxtest.Text += atPDFNumber.ToString())));

               convertToImage(z.FullName);

为什么会这样?可能是我在ConvertToImage类中使用的外部方法也在运行一个线程..在这种情况下,我如何“暂停”我的线程等待ConvertToImage完成......

其他明智的我不知道为什么这个线程的行为方式呢?

4 个答案:

答案 0 :(得分:6)

这是竞争条件。你创建了一个闭包:

() => txtboxtest.Text += atPDFNumber.ToString()

在幕后,编译器会创建一个类似于以下内容的类:

class Closure
{
    public int? adPDFNumber;
    public void Call() {
        txtboxtest.Text += atPDFNumber.ToString();
    }
}

并使用它:

var closure = new Closure();
closure.atPDFNumber = atPDFNumber;
txtboxtest.BeginInvoke(closure.Call);

BeginInvoke实际调用方法在GUI线程中调用,当它发生时,变量atPDFNumber可以增加。您必须在闭包中传递临时变量:

 var tmp = atPDFNumber++;
 txtboxtest.BeginInvoke(() => txtboxtest.Text += tmp.ToString());

<强>更新 在最新版本的C#语言中,这种令人困惑的行为发生了变化。在C#5中,您可以安全地在lambdas中使用循环变量,并且您的代码示例将正常工作。

答案 1 :(得分:4)

您的操作是捕获atPDFNumber变量的值,而不是它的当前值。在您的示例中,如果您将atPDFNumber分配给局部变量并将其传递给您的操作,它将按预期工作。

int atPDFNumber = 0; 
foreach (var z in q)
{
    atPDFNumber++; 
    convertToImage(z.FullName);
    int currentValue = atPDFNumber;
    txtboxtest.BeginInvoke(((Action)(() => txtboxtest.Text += currentValue.ToString())));
}

您可以在此处找到关于Variable Capturing in C#

的信息

答案 2 :(得分:1)

您的第二个样本与第一个样本存在同样的问题,但是由于处理的位置,您会看到更少的问题。无法控制何时运行txtboxtest.Text + = atPDFNumber.ToString()以及在runneing时线程是否没有中断。如果你制作一个更大的测试集,那么看到与第2和第3版相同的结果会有更大的变化。

您看到的问题称为Race condition。你必须使你的委托线程安全。

答案 3 :(得分:0)

如果您想等待另一个帖子,可以使用Thread.Join()方法。

但我相信问题是关闭,请阅读有关closures的更多信息。