我听说线程无法直接访问其他线程的控件。
所以我们的教授给了我们一个片段
private void UpdateUI()
{
if(this.InvokeRequired)
this.Invoke(new MethodInvoker(UpdateUI));
else
this.Refresh();
}
并且说InvokeRequired
属性返回false,线程不是控件的所有者,然后我们应该调用Invoke()
方法告诉所有者线程执行UpdateUI()
方法。然后更新UI。
但最近,出于好奇,我评论了InvokeRequired
和Invoke()
private void UpdateUI()
{
//if(this.InvokeRequired)
//this.Invoke(new MethodInvoker(UpdateUI));
//else
this.Refresh();
}
并且惊讶地看到ThreadPool可以访问另一个线程的控件,现在我觉得我还没有完全理解ThreadPool的概念。
这是完整的代码。
using System;
using System.Threading;
using System.Drawing;
using System.Windows.Forms;
class MainForm : Form
{
public MainForm()
{
this.Text = "Hello WinForms";
ThreadPool.QueueUserWorkItem(Clock);
}
private void Clock(object state)
{
for(;;)
{
Thread.Sleep(1000);
UpdateUI();
}
}
private void UpdateUI()
{
//if(this.InvokeRequired)
// this.Invoke(new MethodInvoker(UpdateUI));
//else
this.Refresh();
}
protected override void OnPaint(PaintEventArgs pe)
{
using(Pen pen = new Pen(Color.Red, 2))
pe.Graphics.DrawRectangle(pen, 20, 20, 125, 30);
pe.Graphics.DrawString(DateTime.Now.ToString(), this.Font, Brushes.Blue, 25, 30);
}
[STAThread]
public static void Main()
{
Application.Run(new MainForm());
}
}
有人可以解释一下这是怎么发生的吗?
感谢。
答案 0 :(得分:3)
ThreadPool
是一个可重用线程池,可用于执行代码。 ThreadPool线程在与UI线程分开的线程上运行。
你关心的是UI线程。任何线程都可以访问UI线程,但是,它不是线程安全。这是关键词。它可能会起作用,也可能不起作用。如果确实如此,那么你就有好运。
非线程安全只是意味着您无法保证一致且预期的行为。现在设置一些东西可能会有效,但是一毫秒之后,它可能会无理由地随机失败。
使用WinForms,.NET默认检查跨线程调用,但我们可以通过将System.Windows.Forms.Control.CheckForIllegalCrossThreadCalls
设置为false
来禁用此功能并在非线程安全上下文中访问UI。但是,禁用此功能意味着我们需要期望并满足随机行为。
答案 1 :(得分:3)
在您发布的应用程序中,您没有从ThreadPool访问任何控件,您只在窗体上调用Refresh。这实际上是向表单发送一条消息,告诉自己重绘自己,但是主消息是在主GUI线程而不是ThreadPool线程上接收的。
因此,您不需要在您的情况下调用,因为您不进行任何跨线程活动。 OnPaint方法通过Windows消息泵间接调用,而不是直接从Refresh方法调用。
如果您要尝试,例如,从后台线程设置文本框的文本...它将引发异常,并且您将需要使用Invoke模式使其工作。
答案 2 :(得分:1)
您可以从其他线程访问元素,但这不安全。
如果多个线程尝试访问您并发控制它会引发异常。因为WF控制不是线程安全的。