【问题标题】:Trycatching invoking of the UI thread尝试调用 UI 线程
【发布时间】:2014-02-10 23:27:53
【问题描述】:

我制作了一个全天候运行的应用,其中三个Backgroundworkers 以不同的间隔运行。

在他们的DoWork 我做了一些Dispatcher.BeginInvoke 所以它更新了一些图表。问题是它在夜间崩溃,我不确定为什么。我已经将 Dispatcher.BeginInvoke 包装在 try/catch 中,但由于我正在调用 UI 线程,我想我可能会尝试使用 Dispatcher.BeginInvoke 来代替 Dispatcher.BeginInvoke

这有关系吗?

【问题讨论】:

    标签: c# wpf exception thread-safety backgroundworker


    【解决方案1】:

    在 RunWorkerCompleted 方法中测试 EventArgs Error 属性是否不为空

    backgroundWorker.RunWorkerCompleted += (s, e) =>
    {
        if (e.Error != null)
        {
            // handle error
        }
    }
    

    try/catch 仅适用于当前线程中发生的事情。如果另一个线程中发生错误,worker 将完成,但你不知道为什么,除非你检查 Error 属性。

    【讨论】:

    • 如果另一个线程发生错误,worker 将完成,但你不知道为什么,除非你检查 Error 属性。 这个说法不正确。 RunWorkerCompletedEventArgs.Error 仅适用于工作线程上抛出的错误,不适用于任何其他线程。 More details。许多人对此表示赞同,我错过了什么吗?
    【解决方案2】:

    使用Dispatcher.BeginInvoke 排队的回调是异步的。您应该观察您传递给Dispatcher.BeginInvoke的委托中的所有异常,因为它们不会在它之外的任何地方传播(除了Application.Current.DispatcherUnhandledExceptionAppDomain.CurrentDomain.UnhandledException 事件和DispatcherOperation.Task.Exception 属性,请参阅以下)。如果他们放任不管,他们将在 UI 线程上的核心 Dispatcher 事件循环内使应用程序崩溃。

    这也包括RunWorkerCompletedEventArgs.Error。在RunWorkerCompletedEvent 事件发生时,Dispatcher.BeginInvoke 委托中抛出的异常将在该处不可用Error

    这里有一个简单的例子来说明这个问题。注意e.ErrorRunWorkerCompleted 中是null

    // UI Thread
    
    // prepare the message window
    var window = new Window 
    { 
        Content = new TextBlock { Text = "Wait while I'm doing the work..." },
        Width = 200,
        Height = 100
    };
    
    // run the worker
    var dispatcher = Dispatcher.CurrentDispatcher;
    var worker = new BackgroundWorker();
    
    worker.DoWork += (s, e) =>
    {
        // do the work 
        Thread.Sleep(1000);
    
        // update the UI
        dispatcher.BeginInvoke(new Action(() =>
        {
            throw new ApplicationException("Catch me if you can!");
        }));
    
        // do more work
        Thread.Sleep(1000);
    };
    
    worker.RunWorkerCompleted += (s, e) =>
    {
        // e.Error will be null
        if (e.Error != null)
            MessageBox.Show("Error: " + e.Error.Message);
        // close the message window
        window.Close();
    };
    
    // start the worker
    worker.RunWorkerAsync();
    
    // show the modal message window 
    // while the worker is working
    window.ShowDialog();
    

    要解决问题,请观察以下异常:

    var step = 0; // progress
    
    // do the work on a background thread
    // ..
    
    var lastStep = step++;
    Dispatcher.BeginInvoke(new Action(() => 
    {
        try 
        {
            // do the UI update
        }
        catch(Exception ex)
        {
            // log or report the error here
            MessageBox.Show("Error during step #" + 
                lastStep + ": " + ex.ToString());
        }
    }));
    

    或者,您可以跟踪Dispatcher.BeginInvoke返回的所有DispatcherOperation

    var invokes = new List<DispatcherOperation>();
    
    // do the work on a background thread
    // ..
    
    invokes.Add(Dispatcher.BeginInvoke(new Action(() => 
        { /* update the UI */ }))));
    

    然后您可以检查DispatcherOperation.Task.ExceptionDispatcher.BeginInvoke 一起排队的每个调用。不过我认为这是不可行的,除非您可以防止invokes 列表无休止地增长。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-11-24
      • 2019-07-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-08-29
      • 2020-08-27
      • 1970-01-01
      相关资源
      最近更新 更多