为什么我们不能从工作线程更新UI?与其他变量/对象相同

Pan*_*wat 4 c# multithreading

想要问一个愚蠢的问题,但我想知道答案。我不需要任何代码即可从工作线程更新UI,我知道如何从工作线程/线程池更新UI。

我想知道为什么每当任何工作线程尝试更新UI控件时,都会出现此错误“跨线程操作无效:从其他线程访问的控件”。当主线程创建的工作线程访问对象并且没有UI交互时,为什么不出现此错误?

见下面的例子

public partial class Form1 : Form
{
    private void Form1_Load(object sender, EventArgs e)
    {
    }

    TextBox textbox2 = null;
    List<int> collection = new List<int>();
    public Form1()
    {
        InitializeComponent();
        textbox2 = new TextBox();
    }
    public void UpdateTextBox()
    {
        collection.Add(Thread.CurrentThread.ManagedThreadId);
        textbox2.Text = "hi, ThreadId: " + Thread.CurrentThread.ManagedThreadId.ToString();
        panel1.Controls.Add(textbox2);//If remove this line... will work with worker thread.
    }
    private void btnMainThread_Click(object sender, EventArgs e)
    {
        UpdateTextBox();
    }
    private void btnWorkerThread_Click(object sender, EventArgs e)
    {
        Thread t1 = new Thread(UpdateTextBox);
        t1.Start();//Will get error. why?
    }
}
Run Code Online (Sandbox Code Playgroud)

我们如何对“ collection”变量应用相同的重构?

Blu*_*rat 5

答案-我想-您正在寻找(为什么以及如何引发异常)是因为该类的属性中存在显式代码,该代码将检查要访问该属性的代码是否在创建该属性的同一线程上运行控制(用户界面线程)。ControlHandle

您可以在此处检查Handle属性的参考源。实际的线程检查发生在属性的实现内部,您也可以在此处进行检查。InvokeRequired

.net Framework的早期版本不包含此检查,因此从其他线程访问用户界面非常容易。我们之所以应该这样做,是因为Win32 API代码库中有很大一部分不是线程安全的,因此从单个线程调用它是确保不会发生并发问题的唯一方法在多线程应用程序中。

Handle在控件内部调用与其相关的任何相关Win32 API函数之前,都将访问该属性(您可以将句柄视为WIN32 API中的“ this”引用),因此,对于集中交叉线程检查。

如果我从您的评论中正确理解,您希望在工作线程尝试更新由另一个线程“拥有”的某个值(不一定是用户界面元素)时模拟相同的行为(引发异常)。在这种情况下,您可以采用框架使用的相同策略

如果您分析的代码InvokeRequired,则只是将当前线程的ID与返回的值进行比较, 如果不匹配SafeNativeMethods.GetWindowThreadProcessId(),则返回true。这将导致在Handle属性的getter访问器中引发跨线程异常。 在您的情况下,您可以存储允许访问变量或资源的线程的ID,如果此存储的ID与尝试访问受保护值的线程的ID不匹配,则可以手动引发异常。