das*_*ash 126
这个问题的答案在于C#Controls如何工作
Windows窗体中的控件绑定到特定线程,并且不是线程安全的.因此,如果从另一个线程调用控件的方法,则必须使用控件的一个调用方法来调用对正确线程的调用.此属性可用于确定是否必须调用invoke方法,如果您不知道哪个线程拥有控件,这可能很有用.
实际上,Invoke所做的是确保您调用的代码出现在控件"依赖"的线程上,从而有效地防止了交叉线程异常.
从历史的角度来看,在.Net 1.1中,这实际上是允许的.这意味着您可以尝试从任何后台线程在"GUI"线程上执行代码,这将主要起作用.有时它会导致您的应用程序退出,因为您在执行其他操作时有效地中断了GUI线程.这是交叉线程异常 - 想象在GUI绘制其他内容时尝试更新TextBox.
实际上,您正在中断队列,这可能会产生许多不可预见的后果.调用实际上是将您想要执行的操作放入该队列的"礼貌"方式,并且此规则是通过抛出的InvalidOperationException从.Net 2.0开始实施的.
要了解幕后实际情况以及"GUI线程"的含义,了解消息泵或消息循环是什么很有用.
这实际上已在" 什么是消息泵 " 的问题中得到解答,并且建议阅读以了解在与控件交互时遇到的实际机制.
您可能觉得有用的其他阅读材料包括
Windows GUI编程的基本规则之一是,只有创建控件的线程才能访问和/或修改其内容(除少数记录的异常外).尝试从任何其他线程执行此操作,您将获得不可预测的行为,从死锁,异常到半更新的UI.从另一个线程更新控件的正确方法是将适当的消息发布到应用程序消息队列.当消息泵到处执行该消息时,控件将在创建它的同一个线程上更新(请记住,消息泵在主线程上运行).
并且,对于具有代表性样本的更多代码重要概述:
// the canonical form (C# consumer)
public delegate void ControlStringConsumer(Control control, string text); // defines a delegate type
public void SetText(Control control, string text) {
if (control.InvokeRequired) {
control.Invoke(new ControlStringConsumer(SetText), new object[]{control, text}); // invoking itself
} else {
control.Text=text; // the "functional part", executing only on the main thread
}
}
Run Code Online (Sandbox Code Playgroud)
一旦您对InvokeRequired有所了解,您可能希望考虑使用扩展方法来包装这些调用.Stack Overflow问题清楚地涵盖了清理代码,需要调用.
Tho*_*que 65
Windows窗体中的控件或窗口对象只是由句柄(有时称为HWND)标识的Win32窗口的包装器.您使用该控件执行的大多数操作最终将导致使用此句柄的Win32 API调用.句柄由创建它的线程(通常是主线程)拥有,不应由另一个线程操纵.如果由于某种原因你需要从另一个线程对控件做一些事情,你可以Invoke用来请求主线程代表你做.
例如,如果要从工作线程更改标签的文本,可以执行以下操作:
theLabel.Invoke(new Action(() => theLabel.Text = "hello world from worker thread!"));
Run Code Online (Sandbox Code Playgroud)
Meh*_*taş 22
如果要修改控件,则必须在创建控件的线程中完成.此Invoke方法允许您在关联的线程(拥有控件的基础窗口句柄的线程)中执行方法.
在下面的示例中,thread1抛出异常,因为SetText1正在尝试从另一个线程修改textBox1.Text.但是在thread2中,SetText2中的Action在创建TextBox的线程中执行
private void btn_Click(object sender, EvenetArgs e)
{
var thread1 = new Thread(SetText1);
var thread2 = new Thread(SetText2);
thread1.Start();
thread2.Start();
}
private void SetText1()
{
textBox1.Text = "Test";
}
private void SetText2()
{
textBox1.Invoke(new Action(() => textBox1.Text = "Test"));
}
Run Code Online (Sandbox Code Playgroud)
Invoke((MethodInvoker)delegate{ textBox1.Text = "Test"; });
Run Code Online (Sandbox Code Playgroud)