使用BackgroundWorker时,wpf应用程序崩溃

时间:2010-10-05 19:55:03

标签: wpf multithreading mvvm

我的视图模型在其构造函数中创建了一个BackgroundWorker。 BackgroundWorker从其DoWork事件处理程序更新模型的属性。下面的代码是一个人为的例子(ViewModelBase几乎是从MVVM文件中逐字记录的)。


public class MyViewModel : ViewModelBase
{
    public int MyProperty
    {
        get
        {
            return this.my_property;
        }
        private set
        {
            if (this.my_property != value)
            {
                this.my_property = value;
                this.OnPropertyChanged("MyProperty");
            }
        }
    }

    public MyViewModel()
    {
        this.worker = new BackgroundWorker();
        this.worker.DoWork += (s, e) => { this.MyProperty = 1; };
        this.worker.RunWorkerAsync();
    }
}

我的视图绑定到此视图模型:


public partial class MyPage : Page
{
    public MyPage()
    {
        InitializeComponent();
        this.DataContext = new MyViewModel();
        }
}

问题是当我的页面显示时,我的应用程序会间歇性地崩溃。这肯定与XAML中的工作线程和数据绑定有关。似乎如果我从页面的Loaded事件处理程序内部启动worker,问题就会消失,但由于难以一致地重现,我不确定这是否是正确的修复。

是否有任何人有任何建议或想法可能是什么原因?

编辑:如果我在没有调试器的情况下运行它,我只能重现它。并且系统日志中的错误是从ShowDialog抛出的InvalidOperationException,这没什么帮助。在调试器下运行正常。

感谢您的帮助 康斯坦丁

5 个答案:

答案 0 :(得分:3)

由于MyProperty不是线程安全的,因此当两个线程同时尝试访问它时会发生奇怪的事情。你应该

  1. 使其线程安全(例如,使用lock关键字)或

  2. 确保其值仅在UI线程中更改。

  3. 我建议采用第二种方法,因为锁定始终是一件棘手的事情。特别是,BackgroundWorker已经支持在工作完成后将值传递回UI线程:

    public void MyViewModel()
    {
        this.worker = new BackgroundWorker();
        this.worker.DoWork += (s, e) => { /* do something */ e.Result = 1; };
        this.worker.RunWorkerCompleted += (s, e) =>
        {
            // all of this happens back in the UI thread
            if (e.Error != null)
            {
                // do some error handling... some exception occurred during DoWork
            }
            else
            {
                this.MyProperty = (int)e.Result;
            }
        };
        this.worker.RunWorkerAsync();
    }
    

答案 1 :(得分:3)

听起来你的后台工作者试图从后台线程更新XAML标记/绑定。您必须调用Dispatcher将值传递回主线程,如下所示:

在你的工作活动中:

Dispatcher.BeginInvoke(delegate()
{
//UI element (test.Text) being updated with value i.ToString() from background thread
test.Text += i.ToString();
});
}

答案 2 :(得分:2)

答案 3 :(得分:1)

WPF中不允许从后台线程触发数据绑定对象上的INotifyPropertyChanged.PropertyChangedINotifyCollectionChanged.CollectionChanged(通常来自ObservableCollection)事件。

您可以通过使用Dispatcher.Invoke设置属性或在DoWork处理程序中完成的任何长时间运行的操作(网络或计算)中返回值后,在RunWorkerCompleted处理程序中设置属性来解决此问题。

答案 4 :(得分:0)

我认为问题是我正在从DoWork事件处理程序内部更新我的属性。在我将更新移动到ProgressChanged处理程序之后,问题似乎已经消失。我甚至可以从ProgressChanged内部更新我的ObservableCollection。所以看起来框架会自动在UI线程上调用ProgressChanged事件处理程序。