我正在使用代理编写示例并更新UI
来自其他threads,
,并遇到了这个问题。
这是我的示例代码;
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线程的控件,并且两者都没有异常调用我的委托和他们的工作,所以如何决定选择哪个控件来调用委托?
答案 0 :(得分:3)
方法Control.BeginInvoke()
在一个创建控件的底层句柄的线程上执行......在你的情况下它应该没有区别。
根据MSDN:
委托是异步调用的,此方法返回 立即。您可以从任何线程,甚至线程调用此方法 拥有控件的句柄。如果控件的句柄不存在 但是,此方法会搜索控件的父链,直到找到它为止 具有窗口句柄的控件或表单。如果不合适 handle可以找到,BeginInvoke会抛出异常。
控件上的大多数方法只能从其中的线程调用 控制创建了。除了InvokeRequired属性之外,还有 是一个线程安全的控件上的四个方法:Invoke, 如果是句柄,则为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)
,当您尝试从与创建Control的方法不同的线程(在我们的示例中来自UI线程)中调用Control的方法时,它可能会抛出异常。因此,最好使用c.BeginInvoke(someMethod)
...
答案 1 :(得分:1)
您需要为Begin / Invoke()指定一个控件,因为Winforms需要确定哪个特定线程需要执行委托目标。它将是创建Handle属性的线程。潜在的winapi电话是GetWindowThreadProcessId。否则,当您将Begin / Invoke()调用为早或晚时,它会吐出子弹。
选择被另一个坚如磐石的规则所取代,顶层窗口的子控件必须在与窗口相同的线程上创建。 Kaboom,如果它不是。
所以它并不重要,你有一个很难保证TextBox和Form都将编组到完全相同的线程。
但最好不要做出选择。当你这样做时,你总是在与一个线程竞争错误作斗争,当线程比窗口存活的时间更长时,没有任何事情发生。这是不可避免的,因为窗口及其控件的使用寿命并非直接由您控制。用户决定何时处置它们。但是你控制线程。 BackgroundWorker和Task类使得理由更加容易。