【发布时间】:2014-11-11 12:58:42
【问题描述】:
我读到无法从另一个线程(不仅是 GUI,而且所有 2 个不同的线程)访问 UI 元素,并看到了如何解决它的代码,但没有找到解释为什么我不能这样做?
【问题讨论】:
标签: c# multithreading user-interface
我读到无法从另一个线程(不仅是 GUI,而且所有 2 个不同的线程)访问 UI 元素,并看到了如何解决它的代码,但没有找到解释为什么我不能这样做?
【问题讨论】:
标签: c# multithreading user-interface
MSDN 不允许这样做的原因:
对 Windows 窗体控件的访问本质上不是线程安全的。如果您有两个或更多线程操作控件的状态,可能会强制控件进入不一致的状态。其他与线程相关的错误也是可能的,包括竞争条件和死锁。确保以线程安全的方式访问您的控件非常重要。
所以从技术上讲,我是可能的(也许他们在设计上忽略了它),但问题是微软的底层代码不是线程安全的。这可能与 Windows 的内部工作原理有关,并且在其 Win32 UI 模型中缺乏线程安全性。
【讨论】:
您不能从除 GUI 线程(主线程)之外的任何其他线程对 GUI 进行更改。话虽如此,如果您想从另一个线程更改控件,则可以毫无问题地从任何线程读取控件(文本框等)的数据,您需要使用类似这样的东西:
textBox1.Invoke((MethodInvoker)(() =>
{
textBox1.Text = "text changed from another thread";
}));
这样做的缺点是,现在您从中调用它的线程被阻塞,直到对文本框的更改完成。如果您不经常从线程更新控件,这不是问题,但如果您想经常这样做,则需要创建第三个线程来根据工作线程正在做什么来处理更新。像这样的
List<string> list = new List<string>();
Thread workThread = new Thread(dowork =>
{
//random work
for(int a = 0; a< 1000000;a++)
{
list.Add(a+" iteration");
Thread.Sleep(10);
}
});
workThread .Start();
bool updateThreadWorking = true;
Thread updateThread = new Thread(dowork =>
{
while(updateThreadWorking)
{
while(list.Count > 0)
{
listBox.Invoke((MethodInvoker)(() =>
listBox.Items.Add(list[0]);
}));
list.RemoveAt(0);
}
Thread.Sleep(200);
}
});
updateThread .Start();
现在工作线程正在全速工作而不会被 UI 阻塞,并且 UI 仍在更新当前状态
【讨论】: