.NET 4.0中的异步代码

时间:2012-11-29 18:48:24

标签: wpf .net-4.0

我在WPF应用程序中运行以下代码:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        this.Loaded += new RoutedEventHandler(MainWindow_Loaded);
    }

    void MainWindow_Loaded(object sender, RoutedEventArgs e)
    {
        object obj = new object();
        Collection.Add(obj);
        Collection.CollectionChanged += new NotifyCollectionChangedEventHandler(delegate(object sender2, NotifyCollectionChangedEventArgs e2)
        {
            if (Collection.Count == 0)
                App.Current.MainWindow.Close();
        });

        Task.Factory.StartNew(() =>
            {
                //Do long running process
                Collection.Remove(obj); //this errors out
            });
    }

    private ObservableCollection<object> Collection = new ObservableCollection<object>();
}

我收到错误System.InvalidOperationException:调用线程无法访问此对象,因为另一个线程拥有它。

我的印象是Task.Factory.StartNew排队了一个异步任务,所以线程应该是相同的,不是吗?

2 个答案:

答案 0 :(得分:3)

Task.Factory.StartNew在默认的TaskScheduler中执行您的操作,因此它将在ThreadPool中运行。

ObservableCollection不是线程安全的。这意味着在UI线程中执行操作的CollectionChanged处理程序(App.Current.MainWindow.Close())不会在UI线程中执行,因为集合修改正在Task的操作中完成,导致错误你正在看到。

如果您只需要与处理程序中的UI进行交互,则可以使用调度程序:

Collection.CollectionChanged += new NotifyCollectionChangedEventHandler(delegate(object sender2, NotifyCollectionChangedEventArgs e2)
        {
            if (Collection.Count == 0)
                this.Dispatcher.BeginInvoke((Action)(()=> App.Current.MainWindow.Close()));
        });

如果需要绑定它,请考虑使用线程安全的实现。见this.

答案 1 :(得分:1)

为了补充Arthur的答案,在我的实际应用程序(不是上面的示例代码)中,我需要从MvvmLight视图模型中执行此操作。从ViewModel访问调度程序:

在内部应用程序中,添加以下内容:

static App()
{
    DispatcherHelper.Initialize();
}

然后调用this.Dispatcher而不是调用DispatcherHelper.UIDispatcher.BeginInvoke((Action)(() => App.Current.MainWindow.Close())); ,因为ViewModel没有对Dispatcher的引用,以下内容将起作用:

{{1}}