Control.Invoke

时间:2017-07-06 13:00:50

标签: c# multithreading winforms

以下代码是简化的概念证明。 在Windows窗体应用程序中,有一个FormClosing事件处理程序来执行一些清理。应用程序启动一些使用Control.Invoke在Form上编写的线程。我知道我可以使用Control.BeginInvoke,但我想更好地了解会发生什么,并找到另一种解决方案。

List<Thread> activeThreadlist;
volatile bool goOnPolling = true;
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
    {
        goOnPolling = false;
        Thread.Sleep(1000);
        foreach (Thread element in activeThreadlist)
            if (element.IsAlive)
                element.Abort();

    }

将一个标志设置为false以便停止线程上的循环,它们有时间终止,如果一个标志仍处于活动状态,它将以中止结束。

以下是其他线程执行的代码:

while (goOnPolling)
{
    //some long elaboration here
    if (!goOnPolling) continue;
    aControl.Invoke(new Action(() =>
    {
        //display some result about the elaboration
    }));
    if (!goOnPolling) continue;
    //some long elaboration here    

}

在表单关闭的大约50%的情况下,线程在Control.Invoke上阻塞,因此它们不会在休眠时间内终止并且调用Abort。我想在调用Invoke之前检查标志 goOnPolling 在99.999%的情况下已经足够了,但我错了。如代码中所述,线程进行了长时间的详细说明(至少100毫秒),因此我预计 goOnPolling 可能会在它们期间发生变化。为什么我错了?是否有另一个简单的解决方案,而不会重复创建一个额外的线程的BeginInvoke?

更新

阅读完评论后,我发现标题错了。我最初写了死锁,但它只是一个意外的(对我而言)阻止条件。一开始我无法理解为什么我的线程在等待终止1000ms之后仍然存在,所以我放了 Abort 来查找。我完全误解了调用 BeginInvoke 之间的区别,谢谢你澄清它。

@Jon B

我同意中断继续更合适,但该代码位于 while(goOnPolling)块内,所以我只能保存一些cpu周期,仅此而已。

在Invoke的早期阶段,为什么经常 goOnPolling 会发生变化?如果我进行长时间的阐述,它应该在大多数时间发生。

1 个答案:

答案 0 :(得分:0)

我们知道这些其他线程希望通过Invoke进入UI线程,并且我们知道您正在绑定UI中的Sleep线程。

这可能是Application.DoEvents最不好选择的少数几次之一:

private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
    goOnPolling = false;
    for(int i=0;i<10;i++)
    {
        Application.DoEvents();
        Thread.Sleep(50);
    }
    //foreach (Thread element in activeThreadlist)
    //    if (element.IsAlive)
    //        element.Abort();
}

并且还改变线程以应对在表单已经关闭之后尝试Invoke而抛出的异常,而不是通过Abort彻底撕掉它们。

至于为什么这么多线程处于他们想要Invoke的状态,从目前为止发布的代码中还不清楚 - 但请记住{{1}正在序列化对UI线程的访问。它是可能所有线程在UI线程到处理之前向Invoke询问的窗口消息转换为“表单关闭”事件,如果对线程的访问非常拥挤。