在多线程操作中调用Control.Invoke / BeginInvoke的位置?

时间:2015-08-12 07:56:30

标签: c# multithreading delegates invoke

我正在使用代理编写示例并更新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);
    }
}

如果我在thistxtSum上调用BeginInvoke方法有什么区别?两者都是来自UI线程的控件,并且两者都没有异常调用我的委托和他们的工作,所以如何决定选择哪个控件来调用委托?

2 个答案:

答案 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类使得理由更加容易。