呼叫控制中心.在多线程操作中调用/BeginInvoke
本文关键字:调用 BeginInvoke 操作 多线程 呼叫控制 | 更新日期: 2023-09-27 18:07:40
我正在编写一个使用委托的示例,并从其他threads,
更新UI
,然后遇到了这个问题。
下面是我的示例代码;
public partial class Form1 : Form
{
private decimal _sum;
private delegate void SetSum();
private readonly SetSum _setSum;
public Form1()
{
InitializeComponent();
_setSum = () =>
{
txtSum.Text = _sum.ToString(CultureInfo.InvariantCulture);
};
}
private void btnLoad_Click(object sender, EventArgs e)
{
new Thread(Method).Start();
}
private void Method()
{
for (decimal i = 0; i < 100000000; i++)
{
_sum += i;
}
//txtSum.BeginInvoke(_setSum);
this.BeginInvoke(_setSum);
}
}
如果我在this
和txtSum
上调用BeginInvoke方法有什么不同?都是来自UI线程的控件,都没有异常调用我的委托和他们的工作很好,那么如何决定选择哪个控件来调用委托呢?
方法Control.BeginInvoke()
在创建控件的底层句柄的线程上执行…在你的情况下,这应该没什么区别。
根据MSDN:
异步调用委托,此方法返回立即。您可以从任何线程(甚至是线程)调用此方法它拥有控件的句柄。如果控件的句柄不存在然而,此方法会搜索控件的父链,直到找到具有窗口句柄的控件或窗体。如果不合适如果找到句柄,则BeginInvoke将抛出异常。
控件上的大多数方法只能从对象所在的线程调用控件已创建。除了invokerrequired属性之外,还有控件上有四个线程安全的方法:方法的句柄是BeginInvoke, EndInvoke和CreateGraphics控件已经创建
来自MSDN的更多信息:https://msdn.microsoft.com/en-us/library/0b1bf3y3%28v=vs.110%29.aspx
想想这个场景:
public partial class Form1 : Form
{
Control c;
public Form1()
{
Task.Factory.StartNew(() => { c = new Control(); });
}
}
如果你使用this.BeginInvoke(someMethod)
,它可能会抛出一个异常,当你试图调用一个控件的方法从不同的线程,而不是一个创建它(从UI线程在我们的情况下)。因此,最好使用c.BeginInvoke(someMethod)
。
您需要指定Begin/Invoke()的控件,因为Winforms需要找出哪个特定的线程需要执行委托目标。它将是创建Handle属性的线程。底层的winapi调用是GetWindowThreadProcessId。否则,当你太早或太晚调用Begin/Invoke()时,它会吐出子弹。
这个选择被另一个坚如磐石的规则所取代,顶层窗口的子控件必须创建在与窗口相同的线程上。
所以这并不重要,你有一个硬保证,TextBox和表单都将封送到完全相同的线程。
但是最好不要做出选择。当您这样做时,您总是在与线程竞争bug作斗争,当线程存活的时间比窗口长一点时,什么都不会发生。这是不可避免的,因为窗口及其控件的寿命并不是直接在您的控制之下。是用户决定什么时候处理它们。但控制线程的是你。BackgroundWorker和Task类让我们更容易理解这一点。