【问题标题】:WPF: issue updating UI from background threadWPF:从后台线程更新 UI 的问题
【发布时间】:2010-05-19 03:16:39
【问题描述】:

我的代码启动了一个后台线程。后台线程进行更改并希望主线程中的 UI 更新。启动线程然后等待的代码如下所示:

                Thread fThread = new Thread(new ThreadStart(PerformSync));

                fThread.IsBackground = true;
                fThread.Start();

                fThread.Join();

                MessageBox.Show("Synchronization complete");

当后台要更新UI时,设置一个StatusMessage,调用如下代码:

    static StatusMessage _statusMessage;
    public delegate void AddStatusDelegate();
    private void AddStatus()
    {
        AddStatusDelegate methodForUIThread = delegate
        {
            _statusMessageList.Add(_statusMessage);
        };

        this.Dispatcher.BeginInvoke(methodForUIThread, System.Windows.Threading.DispatcherPriority.Send);
    }

_statusMessageList 是一个 ObservableCollection,它是 ListBox 的源。

调用了 AddStatus 方法,但主线程上的代码从不执行 - 也就是说,_statusMessage 不会在线程执行时添加到 _statusMessageList。但是,一旦完成(fThread.Join() 返回),主线程上的所有堆叠调用都会被执行。

但是,如果我在调用 fThread.Start() 和 fThread.Join() 之间显示一个消息框,则状态消息会正确更新。

在等待线程终止时,我需要更改哪些内容以便主线程中的代码执行(UI 更新)?

谢谢。

【问题讨论】:

    标签: wpf multithreading


    【解决方案1】:

    fThread.Join 导致您的主线程阻塞,直到后台线程完成。只要主线程被阻塞,UI就无法更新。

    你需要做的是这样的事情(未经测试,但你应该明白):

    void someUiEventThatCausesTheBackgroundProcessingToStart() {
        Thread fThread = new Thread(new ThreadStart(PerformSync));
        fThread.IsBackground = true;
    
        // disable some UI components here, so that the user cannot
        // start the thread a second time
        ...
    
        fThread.Start();
    
        // *don't* call Thread.Join here, so that your main thread does not block!
    }
    
    void PerformSync() {
        try {
            // do your stuff here
            ...
        } finally {
            Dispatcher.Invoke(new Action(ProcessingDone));
        }
    }
    
    void ProcessingDone() {
        // re-enable the UI components
        ...
    
        MessageBox.Show("Synchronization complete");
    }
    

    当然,在 WPF 中,禁用/启用 UI 组件最好使用一些 IsBusyProcessing 依赖属性来完成,该属性与相关 UI 元素的触发器绑定,但这是另一回事...

    编辑:作为另一种选择,您可能希望 check out BackgroundWorker 类,其中包含在主线程中自动触发的 ProgressChangedRunWorkerCompleted 事件。

    【讨论】:

      【解决方案2】:
      fThread.IsBackground = true;
      fThread.Start();
      
      fThread.Join();
      

      对 Join() 的调用会阻塞您的主线程,直到后台线程完成。所以当另一个线程运行时,UI 线程不能做任何事情。您不需要调用 Join()。后台线程结束后,如果需要在主线程上做点什么,就另寻他法吧。

      编辑:如果您在 Start 和 Join 调用之间显示一个消息框,它起作用的原因是因为主线程正在为消息框运行消息循环,而不是在 Join 调用中被阻止。

      【讨论】:

        【解决方案3】:

        BackgroundWorker 是完成这项工作的正确工具。它并不比绝对必要的复杂,为您提供进度报告所需的所有挂钩,并且有大量关于如何使用它的可用信息(例如 this answer)。

        【讨论】:

          猜你喜欢
          • 2012-04-24
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-11-10
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多