呼叫控制中心.在多线程操作中调用/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);
    }
}

如果我在thistxtSum上调用BeginInvoke方法有什么不同?都是来自UI线程的控件,都没有异常调用我的委托和他们的工作很好,那么如何决定选择哪个控件来调用委托呢?

呼叫控制中心.在多线程操作中调用/BeginInvoke

方法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类让我们更容易理解这一点。