我在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
排队了一个异步任务,所以线程应该是相同的,不是吗?
答案 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}}