2

为什么会发生这种情况?

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

在那个类里面有一个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

4 回答 4

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 线程中调用方法 Call,当它发生时,变量 atPDFNumber 可能已经增加。您必须在闭包中传递临时变量:

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

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

于 2012-10-05T08:56:16.630 回答
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())));
}

在这里您可以找到有关C# 中的变量捕获的信息

于 2012-10-05T08:51:52.297 回答
1

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

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

于 2012-10-05T08:56:46.080 回答
0

如果要等待另一个线程,可以使用Thread.Join()方法。

但我认为问题在于闭包,请阅读更多关于闭包的信息。

于 2012-10-05T08:54:59.517 回答