为什么跨线程操作会按照这种方式工作?

10

已解决

看起来 Oliver 是正确的。经过多次尝试,我得到了异常,在调试模式下我确信得到了它。所以这一切都与时序有关。您还应该检查 Matthew Watson 的回答 ;)

示例

首先是一个小例子,它将解释我的困惑。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;

namespace testCrossThreading
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            new Thread(ThreadJob).Start();
        }

        void ThreadJob()
        {
            //label1.Text = "1";
            changeText(label1, "1");
        }

        void changeText(Label L, String message)
        {
            L.Text = message;
        }
    }
}

问题

现在我的问题是:如果我在“ThreadJob”函数中取消注释label1.Text = "1";,那么我会像预期的那样得到一个跨线程异常。 但是,如果我像示例所示将它保持已注释状态,则它可以正常工作。但是为什么呢? 该函数由子线程调用,我没有调用任何内容。因此,在我看来,仍然是子线程而不是GUI线程更改标签文本。还是我漏掉了什么?

我会这样写。

void ThreadJob()
        {
            Action a = () => label1.Text = "1";
            this.Invoke(a);
        }

1
你非常确定发布的版本不会抛出异常吗?在调试模式下运行了吗? - H H
5
我将您的代码复制到一个新项目中,像我预料的那样,在L.Text = message;上出现了跨线程异常。我看不出它如何能为您工作。 - MrBlue
在调试模式下,它抛出了一个异常。难道 VC 2008 不能正确处理它吗? - lorenz albert
2个回答

7

我认为这只是一个时间问题。如果您尝试从非GUI线程更新GUI元素,则可能会引发跨线程异常。您甚至可以通过调用来禁用所有跨线程异常

Form.CheckForIllegalCrossThreadCalls = false;

但是一旦异常消失,进一步的行为就未定义,并可能导致非常微妙的错误。因此,将异常视为代码异味的提示,但请注意,即使应该抛出异常,有时也不会抛出异常。


1
是的,禁用只是隐藏了错误。不确定为什么这个功能会存在。 - usr
哦,之前没有注意到。我无法想象它会带来什么好处。 - MrBlue

2

我认为你可能存在竞态条件,这就是为什么结果会有所不同的原因。

如果您尝试更改当前未显示的控件的Text属性,则.Net不关心哪个线程进行更改。

在您的代码中,您从构造函数开始一个线程。实际显示窗体的代码可能已经在设置属性的线程代码之前显示了它,也可能没有。

当您调用额外的函数来设置属性时,时间会发生变化并暴露出竞态条件。

您可以通过将ThreadJob()的开头添加Thread.Sleep(100)来进行测试。


是的,你说得对。这让我想起了一个问题,我试图在程序的最开始调用某些东西,但此时控件句柄尚未创建,这也引发了一个只有通过幸运的时间或强制使用Control.create()来先创建所有句柄才能解决的异常情况 ^^ - lorenz albert
这非常类似于我曾经遇到的一个可怕问题,即在执行 Invoke() 之前先执行 if (control.InvokeRequired) - 因为线程在测试时,主 UI 线程正在创建包含控件的窗口,所以 control.InvokeRequired 的状态在我测试它和对结果进行操作之间发生了变化! - Matthew Watson

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接