Eri*_*bes 46 c# multithreading invoke invokerequired
我知道,当从任何非UI线程操作UI控件时,您必须封送对UI线程的调用以避免问题.一般的共识是您应该使用测试InvokeRequired,如果为true,则使用.Invoke来执行封送处理.
这会导致很多代码看起来像这样:
private void UpdateSummary(string text)
{
if (this.InvokeRequired)
{
this.Invoke(new Action(() => UpdateSummary(text)));
}
else
{
summary.Text = text;
}
}
Run Code Online (Sandbox Code Playgroud)
我的问题是:我可以省略InvokeRequired测试并只调用Invoke,如下所示:
private void UpdateSummary(string text)
{
this.Invoke(new Action(() => summary.Text = text));
}
Run Code Online (Sandbox Code Playgroud)
这样做有问题吗?如果是这样,是否有更好的方法来保持InvokeRequired测试,而不必在整个地方复制和粘贴此模式?
Joh*_*zen 65
那怎么样:
public static class ControlHelpers
{
public static void InvokeIfRequired<T>(this T control, Action<T> action) where T : ISynchronizeInvoke
{
if (control.InvokeRequired)
{
control.Invoke(new Action(() => action(control)), null);
}
else
{
action(control);
}
}
}
Run Code Online (Sandbox Code Playgroud)
像这样使用它:
private void UpdateSummary(string text)
{
summary.InvokeIfRequired(s => { s.Text = text });
}
Run Code Online (Sandbox Code Playgroud)
Invoke从UI线程调用有点效率低下.
相反,您可以创建一个InvokeIfNeeded带Action参数的扩展方法.(这也可以让你new Action(...)从呼叫网站中删除)
我一直在阅读关于添加逻辑检查来反复讨论的问题,以确定在不在UI线程上而不在UI线程本身上时是否应该使用IFF调用.我写了一个类来检查执行(通过秒表)各种方法的时间,以粗略估计一种方法相对于另一种方法的效率.
对于你们中的一些人来说结果可能会令人惊讶(这些测试是通过Form.Shown事件运行的):
// notice that we are updating the form's title bar 10,000 times
// directly on the UI thread
TimedAction.Go
(
"Direct on UI Thread",
() =>
{
for (int i = 0; i < 10000; i++)
{
this.Text = "1234567890";
}
}
);
// notice that we are invoking the update of the title bar
// (UI thread -> [invoke] -> UI thread)
TimedAction.Go
(
"Invoke on UI Thread",
() =>
{
this.Invoke
(
new Action
(
() =>
{
for (int i = 0; i < 10000; i++)
{
this.Text = "1234567890";
}
}
)
);
}
);
// the following is invoking each UPDATE on the UI thread from the UI thread
// (10,000 invokes)
TimedAction.Go
(
"Separate Invoke on UI Thread",
() =>
{
for (int i = 0; i < 10000; i++)
{
this.Invoke
(
new Action
(
() =>
{
this.Text = "1234567890";
}
)
);
}
}
);
Run Code Online (Sandbox Code Playgroud)
结果如下:
我的结论是,您可以随时安全地调用,无论您是在UI线程还是工作线程,都没有通过消息泵循环回来的显着开销.但是,在UI线程上执行大部分工作而不是多次调用UI线程(通过Invoke())是有利的并且大大提高了效率.
我意识到已经有一个答案,但是我也想发布它(我也在这里发布).
我的稍微不同,它可以稍微更安全地处理空控件,并在必要时返回结果.当尝试调用在父窗体上显示可能为null的MessageBox并返回显示MessageBox的DialogResult时,这两个对我来说都派上了用场.
using System;
using System.Windows.Forms;
/// <summary>
/// Extension methods acting on Control objects.
/// </summary>
internal static class ControlExtensionMethods
{
/// <summary>
/// Invokes the given action on the given control's UI thread, if invocation is needed.
/// </summary>
/// <param name="control">Control on whose UI thread to possibly invoke.</param>
/// <param name="action">Action to be invoked on the given control.</param>
public static void MaybeInvoke(this Control control, Action action)
{
if (control != null && control.InvokeRequired)
{
control.Invoke(action);
}
else
{
action();
}
}
/// <summary>
/// Maybe Invoke a Func that returns a value.
/// </summary>
/// <typeparam name="T">Return type of func.</typeparam>
/// <param name="control">Control on which to maybe invoke.</param>
/// <param name="func">Function returning a value, to invoke.</param>
/// <returns>The result of the call to func.</returns>
public static T MaybeInvoke<T>(this Control control, Func<T> func)
{
if (control != null && control.InvokeRequired)
{
return (T)(control.Invoke(func));
}
else
{
return func();
}
}
}
Run Code Online (Sandbox Code Playgroud)
用法:
myForm.MaybeInvoke(() => this.Text = "Hello world");
// Sometimes the control might be null, but that's okay.
var dialogResult = this.Parent.MaybeInvoke(() => MessageBox.Show(this, "Yes or no?", "Choice", MessageBoxButtons.YesNo));
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
16229 次 |
| 最近记录: |