Tom*_*lis 178 c# multithreading thread-safety invokerequired winforms
我已经痛苦地意识到需要在事件驱动的GUI代码中编写以下代码模式的频率,其中
private void DoGUISwitch() {
// cruisin for a bruisin' through exception city
object1.Visible = true;
object2.Visible = false;
}
Run Code Online (Sandbox Code Playgroud)
变为:
private void DoGUISwitch() {
if (object1.InvokeRequired) {
object1.Invoke(new MethodInvoker(() => { DoGUISwitch(); }));
} else {
object1.Visible = true;
object2.Visible = false;
}
}
Run Code Online (Sandbox Code Playgroud)
这是C#中的一个尴尬模式,无论是记忆还是打字.有没有人提出某种捷径或构造,在一定程度上自动化?如果有一种方法可以将函数附加到执行此检查的对象,而不必经历所有这些额外的工作,如object1.InvokeIfNecessary.visible = true类型快捷方式,那就太酷了.
上一页答案已经讨论的只是打电话的invoke()每次不切实际,甚则调用()语法既效率低下,仍然尴尬应对.
那么,有没有人想出任何捷径?
Oli*_*bes 135
李的方法可以进一步简化
public static void InvokeIfRequired(this Control control, MethodInvoker action)
{
// See Update 2 for edits Mike de Klerk suggests to insert here.
if (control.InvokeRequired) {
control.Invoke(action);
} else {
action();
}
}
Run Code Online (Sandbox Code Playgroud)
并且可以像这样调用
richEditControl1.InvokeIfRequired(() =>
{
// Do anything you want with the control here
richEditControl1.RtfText = value;
RtfHelpers.AddMissingStyles(richEditControl1);
});
Run Code Online (Sandbox Code Playgroud)
无需将控件作为参数传递给委托.C#自动创建一个闭包.
更新:
根据其他几个海报Control可以概括为ISynchronizeInvoke:
public static void InvokeIfRequired(this ISynchronizeInvoke obj,
MethodInvoker action)
{
if (obj.InvokeRequired) {
var args = new object[0];
obj.Invoke(action, args);
} else {
action();
}
}
Run Code Online (Sandbox Code Playgroud)
DonBoitnott指出,不同于Control所述ISynchronizeInvoke接口需要用于一个对象数组Invoke方法作为参数列表action.
更新2
Mike de Klerk建议的编辑(请参阅第1个代码段中的插入点评论):
// When the form, thus the control, isn't visible yet, InvokeRequired returns false,
// resulting still in a cross-thread exception.
while (!control.Visible)
{
System.Threading.Thread.Sleep(50);
}
Run Code Online (Sandbox Code Playgroud)
有关此建议的疑虑,请参阅下面的ToolmakerSteve评论.
Lee*_*Lee 133
你可以写一个扩展方法:
public static void InvokeIfRequired(this Control c, Action<Control> action)
{
if(c.InvokeRequired)
{
c.Invoke(new Action(() => action(c)));
}
else
{
action(c);
}
}
Run Code Online (Sandbox Code Playgroud)
并像这样使用它:
object1.InvokeIfRequired(c => { c.Visible = true; });
Run Code Online (Sandbox Code Playgroud)
编辑:正如Simpzon在评论中指出的那样,您还可以将签名更改为:
public static void InvokeIfRequired<T>(this T c, Action<T> action)
where T : Control
Run Code Online (Sandbox Code Playgroud)
Mat*_*vis 33
这是我在所有代码中使用的表单.
private void DoGUISwitch()
{
Invoke( ( MethodInvoker ) delegate {
object1.Visible = true;
object2.Visible = false;
});
}
Run Code Online (Sandbox Code Playgroud)
我已经在博客条目基于此这里.我没有让这种方法让我失望,所以我认为没有理由通过检查InvokeRequired属性来使我的代码复杂化.
希望这可以帮助.
创建一个ThreadSafeInvoke.snippet文件,然后你可以选择更新语句,右键单击并选择'Surround With ...'或Ctrl-K + S:
<?xml version="1.0" encoding="utf-8" ?>
<CodeSnippet Format="1.0.0" xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
<Header>
<Title>ThreadsafeInvoke</Title>
<Shortcut></Shortcut>
<Description>Wraps code in an anonymous method passed to Invoke for Thread safety.</Description>
<SnippetTypes>
<SnippetType>SurroundsWith</SnippetType>
</SnippetTypes>
</Header>
<Snippet>
<Code Language="CSharp">
<![CDATA[
Invoke( (MethodInvoker) delegate
{
$selected$
});
]]>
</Code>
</Snippet>
</CodeSnippet>
Run Code Online (Sandbox Code Playgroud)
我宁愿使用 Delegate 方法的单个实例,而不是每次都创建一个新实例。在我的情况下,我曾经显示来自 Backroundworker 的进度和(信息/错误)消息,从 sql 实例复制和投射大数据。每次在大约 70000 个进度和消息调用之后,我的表单停止工作并显示新消息。当我开始使用单个全局实例委托时,这并没有发生。
delegate void ShowMessageCallback(string message);
private void Form1_Load(object sender, EventArgs e)
{
ShowMessageCallback showMessageDelegate = new ShowMessageCallback(ShowMessage);
}
private void ShowMessage(string message)
{
if (this.InvokeRequired)
this.Invoke(showMessageDelegate, message);
else
labelMessage.Text = message;
}
void Message_OnMessage(object sender, Utilities.Message.MessageEventArgs e)
{
ShowMessage(e.Message);
}
Run Code Online (Sandbox Code Playgroud)
这是Lee's,Oliver和Stephan的答案的改进/组合版本.
public delegate void InvokeIfRequiredDelegate<T>(T obj)
where T : ISynchronizeInvoke;
public static void InvokeIfRequired<T>(this T obj, InvokeIfRequiredDelegate<T> action)
where T : ISynchronizeInvoke
{
if (obj.InvokeRequired)
{
obj.Invoke(action, new object[] { obj });
}
else
{
action(obj);
}
}
Run Code Online (Sandbox Code Playgroud)
该模板允许灵活且无需编译的代码,当专用代表提供效率时,该代码更易读.
progressBar1.InvokeIfRequired(o =>
{
o.Style = ProgressBarStyle.Marquee;
o.MarqueeAnimationSpeed = 40;
});
Run Code Online (Sandbox Code Playgroud)
用法:
control.InvokeIfRequired(c => c.Visible = false);
return control.InvokeIfRequired(c => {
c.Visible = value
return c.Visible;
});
Run Code Online (Sandbox Code Playgroud)
代码:
control.InvokeIfRequired(c => c.Visible = false);
return control.InvokeIfRequired(c => {
c.Visible = value
return c.Visible;
});
Run Code Online (Sandbox Code Playgroud)