为什么Thread.Join()在UI线程上调用时不挂起我的应用程序?

Ami*_*shi 6 c# multithreading

根据我的理解(我在线程方面不擅长),Join()会阻塞调用线程,直到调用Join()的线程返回.

如果这是真的并且从UI线程调用了Join(),则为某些长时间运行的操作创建新线程没有任何意义.有些问题,那些问为什么Join()挂起应用程序.这看起来很自然.

顺便说一句,即使它看起来很自然,我的应用程序也没有相应的行为.它不会挂起我的应用程序.

没有挂起应用程序的线程的代码: -

string retValue = "";
retValue = LongRunningHeavyFunction();
txtResult.Text = retValue;
Run Code Online (Sandbox Code Playgroud)

带有不挂应用程序的线程的代码: -

string retValue = "";
Thread thread = new Thread(
() =>
{
    retValue = LongRunningHeavyFunction();
});
thread.Start();
thread.Join();
txtResult.Text = retValue;
Run Code Online (Sandbox Code Playgroud)

上面的代码很好,没有挂起应用程序 函数调用大约需要15-20秒.为什么应用程序不挂起?

这对我来说不是问题; 实际上这是一个好消息.但我只是不明白它有什么不同?它与我阅读和学习的内容不符.

如果重要的话,我正在使用DotNet Framework 4.0.

Han*_*ant 17

由于[STAThread]Main()入口点上的属性,程序的UI线程创建了一个STA,一个单线程单元.还要查看Thread.SetApartmentState().STA对非线程安全的代码非常友好,通常是您无法看到的代码,因为它是另一个程序的一部分.

就像将数据放在剪贴板上的代码一样,在Drag + Drop操作中提供数据,在显示OpenFiledialog时激活的shell扩展,像WebBrowser这样的控件需要客户端代码才能拥有调度程序以便它可以提升DocumentCompleted事件,侦听通知的Windows挂钩,依赖可访问性api实现UI自动化的代码或屏幕阅读器.等等.

即使涉及不同进程中的多个线程,也不需要该代码是线程安全的.一般来说编写线程安全的代码很困难,因为没有任何好的方法让作者测试它,所以它变得非常困难.他无法对可能激活其代码的每个可能程序进行测试.公寓是为解决这个问题而发明的,无需编写线程安全的代码.

STA是您的承诺.你发誓,交叉你的心 - 希望死的风格,你的UI线程表现良好,它必须有一个调度程序(也称为泵消息循环),绝不能阻止.调度程序是以线程安全的方式运行代码所必需的,它是生产者 - 消费者问题的通用解决方案.而且你必须永远不要阻止,因为这样做很可能会导致其他程序员代码死锁.您无法调试的死锁类型,因为您没有该代码的源代码.Winforms,WPF或Modern UI程序的UI线程始终实现此承诺,这是UI线程与您可能使用的任何其他线程不同的基本原因.

足够的介绍,Thread.Join()调用块,从而违反了STA合同.这是非常糟糕的,所以CLR设计师做了一些事情.正如您所发现的那样,他们通过阻塞来实现Join(),因为它是在STA线程上调用的.

他们通过实现Application.DoEvents()的风格来实现这一点.这是一种非常臭名昭着的方法,可以避免UI线程无法响应.它可能导致很难诊断重入错误.它们的版本并不像DoEvents()那么糟糕,它对于允许调度哪种消息以最小化风险非常有选择性.

这通常没问题,但是从来没有你想要有意测试自己.重新入侵的错误是非常严重的错误,与线程竞争错误一样糟糕.因此,您应该永远不要在UI线程上使用Thread.Join()..NET Framework为BackgroundWorker和Task类提供了很好的替代方案,它们都为您提供了在线程完成时执行代码(无论是 Join()调用之后)的非常好的方法.