使用BackgroundWorker实现启动屏时跨线程关闭窗口报错求助
问题分析与解决
核心问题
错误提示的本质是WPF控件只能由创建它的线程访问:你的启动屏Splash_Screen在自定义STA线程中创建,但RunWorkerCompleted事件在主线程触发,直接在主线程调用splash.Close()会触发跨线程访问错误。另外代码存在重复绑定RunWorkerCompleted事件的冗余问题。
修复步骤
1. 移除重复事件绑定
删掉代码中重复的worker.RunWorkerCompleted += Worker_RunWorkerCompleted;语句(保留第一处绑定即可)。
2. 正确在启动屏所属线程执行关闭操作
通过启动屏线程的Dispatcher执行关闭逻辑,确保操作在控件创建线程上运行,同时正确终止线程的消息循环。
修复后的完整代码
BackgroundWorker worker; Splash_Screen splash; Thread splashThread; this.Hide(); worker = new BackgroundWorker(); worker.DoWork += Worker_DoWork; worker.RunWorkerCompleted += Worker_RunWorkerCompleted; worker.RunWorkerAsync(); // 此处放置非耗时的初始化逻辑,耗时操作请移至DoWork内 private void Worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { // 通过启动屏线程的Dispatcher执行关闭操作 splashThread?.Dispatcher.Invoke(() => { splash.Close(); System.Windows.Threading.Dispatcher.CurrentDispatcher.InvokeShutdown(); }); this.Show(); this.Activate(); } private void Worker_DoWork(object sender, DoWorkEventArgs e) { StaThreadWrapper(() => { splash = new Splash_Screen(); splash.Show(); }); // 此处放置真正的耗时加载操作,如数据库连接、文件读取等 // LoadHeavyAppData(); } private void StaThreadWrapper(Action action) { splashThread = new Thread(o => { action(); System.Windows.Threading.Dispatcher.Run(); }); splashThread.SetApartmentState(ApartmentState.STA); splashThread.Start(); }
关键说明
- 跨线程操作控件:所有对
splash的操作(包括关闭)必须通过它所属线程的Dispatcher.Invoke执行,保证操作在控件创建线程上运行。 - 终止Dispatcher循环:关闭启动屏后调用
InvokeShutdown(),可正确终止启动屏线程的消息循环,避免线程残留。 - 耗时操作位置:如果应用加载逻辑是耗时操作,必须移至
DoWork事件内,否则主线程会阻塞,启动屏无法正常显示,且RunWorkerCompleted会提前触发。
内容的提问来源于stack exchange,提问作者dadou
相关产品推荐
相关产品推荐

