我已经痛苦地意识到需要在事件驱动的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()每次不切实际,甚则调用()语法既效率低下,仍然尴尬应对.
那么,有没有人想出任何捷径?
我知道,当从任何非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测试,而不必在整个地方复制和粘贴此模式?
在这篇文章中:
http://msdn.microsoft.com/en-us/library/ms171728(VS.80).aspx
作者使用以下方法对Windows窗体控件进行线程安全调用:
private void SetText(string text)
{
// InvokeRequired required compares the thread ID of the
// calling thread to the thread ID of the creating thread.
// If these threads are different, it returns true.
if (this.textBox1.InvokeRequired)
{
SetTextCallback d = new SetTextCallback(SetText);
this.Invoke(d, new object[] { text });
}
else
{
this.textBox1.Text = text;
}
}
Run Code Online (Sandbox Code Playgroud)
是否有更短的方法来完成同样的事情?
我是一名新手程序员,所以我可能在这里完全弄错了,但是这个问题让我更加困惑.
这实际上是这个问题的后续行动.
接受的答案是,你必须调用InvokeRequired以避免一些开销,因为你有可能已经在UI线程上运行了.
理论上,我同意它可以节省一些时间.经过一些测试后,我发现使用Invoke所需的时间比正常调用操作大两倍(测试如设置标签文本n次,或在RichTextBox中放置一个非常非常大的字符串).
但!然后有练习.
MSDN文档说:
此属性可用于确定是否必须调用invoke方法,如果您不知道哪个线程拥有控件,这可能很有用.
在大多数情况下,您确实知道何时尝试从另一个线程访问控件.实际上,我能想到的唯一情况是,从一个可以被线程X调用的方法以及所有者线程访问控件时.对我来说这是一个非常不可能的情况.
即使你真的不知道哪个线程试图操纵控件,也有一个事实是UI线程不必经常更新.对于您的GUI,25-30 fps之间的任何内容都应该没问题.并且在UI控件中进行的大多数更改所花费的时间远远少于毫秒.
因此,如果我理解正确,那么您必须检查是否需要调用的唯一情况是您何时不知道哪个线程正在访问控件以及何时GUI更新需要超过40毫秒才能完成.
然后我在http://programmers.stackexchange.com上询问了这个问题的答案.这表明,当您不需要它时,您不应该忙于过早优化.特别是如果它牺牲了代码的可读性.
所以这让我想到了一个问题:当你知道一个不同的线程访问一个控件时,你不应该只使用invoke,只有当你知道你的UI线程可以访问那段代码并且你发现它应该运行得更快时,你才应检查是否需要调用?
PS:在校对我的问题后,听起来真的像我在咆哮.但实际上我只是好奇为什么InvokeRequired似乎被许多比我更有经验的程序员所滥用.
我的同事喜欢这样做
if (!listbox1.InvokeRequired)
listbox1.Items.Add(Container.error_message);
else
listbox1.Invoke((MethodInvoker)delegate
{
listbox1.Items.Add(Container.error_message);
});
Run Code Online (Sandbox Code Playgroud)
他为什么要检查InvokedRequired?仅使用此声明会更好吗?
listbox1.Invoke((MethodInvoker)delegate
{
listbox1.Items.Add(Container.error_message);
});
Run Code Online (Sandbox Code Playgroud)