在非初始线程执行Application.Run()有哪些后果及注意事项?
问题解答
1. 该写法是否会引发不可预见的问题?
存在几个明确的风险点:
- 你从DI容器获取
_mainMenuView的线程如果不是你新创建的这个STA线程,会导致窗体的窗口句柄绑定到非UI线程,后续所有UI操作都会触发跨线程异常。WinForms要求窗体必须在其所属的STA线程中创建。 - 多UI线程场景下,不同线程创建的窗体不能设置父子/所有者关系,也不能直接跨线程访问,很容易出现误操作导致崩溃。
- 如果DI容器中的服务为单例/作用域生命周期,被多个线程同时访问时没有做线程安全处理,会出现状态异常。
Application.Exit()默认只会终止调用线程的消息循环,多UI线程场景下需要单独处理每个UI线程的退出逻辑,否则容易出现进程无法正常退出的问题。
2. 是否需要将该线程Join回主线程?
取决于你应用的生命周期设计:
- 如果你期望主窗体关闭后整个应用进程才退出,就需要在通用宿主的主线程中
Join这个UI线程,否则宿主主线程跑完启动逻辑后会直接结束,进程会随之退出,窗体刚启动就会被销毁。 - 也可以选择将WinForms的生命周期和通用宿主的
IHostApplicationLifetime绑定,监听主窗体关闭事件后调用StopAsync停止宿主,不需要手动Join也可以实现生命周期同步。
3. 关闭对应窗体后该线程是否会自动终止?
会自动终止。你当前的写法中线程入口是Application.Run(Form),当传入的窗体被调用Close()关闭后,该线程的消息循环会自动退出,委托执行完毕后线程就会被释放。如果仅调用Hide()隐藏窗体,消息循环不会退出,线程也不会终止。
4. 通过该接口读写窗体属性是否安全?是否需要处理跨线程Invocation?
不安全,必须做跨线程封送处理。
接口只是对窗体实现的抽象,不会改变WinForms的线程访问规则:只要不是在窗体所属的STA线程中访问UI属性/方法,无论通过什么类型调用,都属于非法跨线程操作,轻则抛出异常,重则出现不可预见的UI渲染异常、死锁。所有跨线程的UI操作都需要通过窗体的Invoke/BeginInvoke方法封送到UI线程执行。
内容的提问来源于stack exchange,提问作者Finn
相关产品推荐
相关产品推荐

