C# 线程安全控件更新
C# thread safe control update
问题:当窗体及其控件从线程访问时,哪个更合适?:
调用表单并在表单调用中更新其控件
清晰地调用控件和清晰地更新控件
代码
if (mainForm.InvokeRequired)
{
mainForm.Invoke(
(Action)(() =>
{
mainForm.label1.Text = "update";
mainForm.textBox1.Text = "update";
mainForm.button1.Text = "update";
}));
}
else
{
mainForm.label1.Text = "update";
mainForm.textBox1.Text = "update";
mainForm.button1.Text = "update";
}
//OR
if (mainForm.label1.InvokeRequired)
{
mainForm.Invoke((Action)(() => { mainForm.label1.Text = "update"; }));
}
else
{
mainForm.label1.Text = "update";
}
每次您 Invoke
,都会有延迟和成本,因为我们必须向 UI 线程发送消息,本质上消息是 "please run this code"。然后我们必须等待 UI 线程接收此消息(它当前可能正忙于做一些其他工作),运行 我们要求它接收的代码,并等待它指示就这样完成了。然后,最后,我们可以在 Invoke
通话后继续工作。
现在,在宏伟的计划中,这个成本是相当低的。因此,如果您只是使用 Invoke
一次或两次,则不值得考虑。但是,如果您计划执行一个大循环并在循环内 Invoke
ing,您可能需要暂停并重新考虑。通常最好将 Invoke
移到循环之外。
但是,UI线程是一种宝贵的资源。我们不想在该线程上花费时间来执行 非 UI 相关任务。
因此,如果您有一个循环同时执行 CPU 密集型工作和 UI 更新,则两者都不一定合适。在这种情况下,看看您是否可以重新构建代码,以便在循环内准备 批次 的 UI 更新,然后偶尔(或在循环之后,如果可能)做一个 Invoke
来执行那些 UI 更新。
在您的情况下,您似乎已经有一批 UI 更新要执行。所以更喜欢 Invoke
一次完成所有三个的代码:
if (mainForm.InvokeRequired)
{
mainForm.Invoke(
(Action)(() =>
{
mainForm.label1.Text = "update";
mainForm.textBox1.Text = "update";
mainForm.button1.Text = "update";
}));
}
else
{
mainForm.label1.Text = "update";
mainForm.textBox1.Text = "update";
mainForm.button1.Text = "update";
}
但请注意,通常最好构建代码,这样您就不会 复制 上面的实际 UI 更新。类似于:
private void DoUpdate()
{
if(mainForm.InvokeRequired)
{
mainForm.Invoke(DoUpdate);
return;
}
mainForm.label1.Text = "update";
mainForm.textBox1.Text = "update";
mainForm.button1.Text = "update";
}
我通常使用ISynchronizeInvoke因为所有forms/usercontrols都继承了这个接口,然后调用一个方法在ui线程上执行你的动作(如果调用是requi红色),
public void SafeExecute(IView form, Action methodToExecute)
{
// args chek....
ISynchronizeInvoke view = form as ISynchronizeInvoke;
if(view != null)
{
if (!View.InvokeRequired)
{
methodToExecute();
}
else
{
view.Invoke(methodToExecute, null);
}
}
else methodToExecute();
}
然后在你的代码隐藏中你可以像这样调用这个方法(我假设你的视图实现了一个接口,在这种情况下我称之为 IView)
SafeExecute(this, () => {
mainForm.label1.Text = "update";
mainForm.textBox1.Text = "update";
mainForm.button1.Text = "update";
})
问题:当窗体及其控件从线程访问时,哪个更合适?:
调用表单并在表单调用中更新其控件
清晰地调用控件和清晰地更新控件
代码
if (mainForm.InvokeRequired)
{
mainForm.Invoke(
(Action)(() =>
{
mainForm.label1.Text = "update";
mainForm.textBox1.Text = "update";
mainForm.button1.Text = "update";
}));
}
else
{
mainForm.label1.Text = "update";
mainForm.textBox1.Text = "update";
mainForm.button1.Text = "update";
}
//OR
if (mainForm.label1.InvokeRequired)
{
mainForm.Invoke((Action)(() => { mainForm.label1.Text = "update"; }));
}
else
{
mainForm.label1.Text = "update";
}
每次您 Invoke
,都会有延迟和成本,因为我们必须向 UI 线程发送消息,本质上消息是 "please run this code"。然后我们必须等待 UI 线程接收此消息(它当前可能正忙于做一些其他工作),运行 我们要求它接收的代码,并等待它指示就这样完成了。然后,最后,我们可以在 Invoke
通话后继续工作。
现在,在宏伟的计划中,这个成本是相当低的。因此,如果您只是使用 Invoke
一次或两次,则不值得考虑。但是,如果您计划执行一个大循环并在循环内 Invoke
ing,您可能需要暂停并重新考虑。通常最好将 Invoke
移到循环之外。
但是,UI线程是一种宝贵的资源。我们不想在该线程上花费时间来执行 非 UI 相关任务。
因此,如果您有一个循环同时执行 CPU 密集型工作和 UI 更新,则两者都不一定合适。在这种情况下,看看您是否可以重新构建代码,以便在循环内准备 批次 的 UI 更新,然后偶尔(或在循环之后,如果可能)做一个 Invoke
来执行那些 UI 更新。
在您的情况下,您似乎已经有一批 UI 更新要执行。所以更喜欢 Invoke
一次完成所有三个的代码:
if (mainForm.InvokeRequired)
{
mainForm.Invoke(
(Action)(() =>
{
mainForm.label1.Text = "update";
mainForm.textBox1.Text = "update";
mainForm.button1.Text = "update";
}));
}
else
{
mainForm.label1.Text = "update";
mainForm.textBox1.Text = "update";
mainForm.button1.Text = "update";
}
但请注意,通常最好构建代码,这样您就不会 复制 上面的实际 UI 更新。类似于:
private void DoUpdate()
{
if(mainForm.InvokeRequired)
{
mainForm.Invoke(DoUpdate);
return;
}
mainForm.label1.Text = "update";
mainForm.textBox1.Text = "update";
mainForm.button1.Text = "update";
}
我通常使用ISynchronizeInvoke因为所有forms/usercontrols都继承了这个接口,然后调用一个方法在ui线程上执行你的动作(如果调用是requi红色),
public void SafeExecute(IView form, Action methodToExecute)
{
// args chek....
ISynchronizeInvoke view = form as ISynchronizeInvoke;
if(view != null)
{
if (!View.InvokeRequired)
{
methodToExecute();
}
else
{
view.Invoke(methodToExecute, null);
}
}
else methodToExecute();
}
然后在你的代码隐藏中你可以像这样调用这个方法(我假设你的视图实现了一个接口,在这种情况下我称之为 IView)
SafeExecute(this, () => {
mainForm.label1.Text = "update";
mainForm.textBox1.Text = "update";
mainForm.button1.Text = "update";
})