以下代码是简化的概念证明。 在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 会发生变化?如果我进行长时间的阐述,它应该在大多数时间发生。
答案 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
询问的窗口消息转换为“表单关闭”事件,如果对线程的访问非常拥挤。