c# – WPF,TPL,生产者/消费者模式 – 错误的线程错误
我是TPL和WPf的新手,有以下问题.
我尝试在无限循环中下载一个站点(这里只是for循环)和 将其添加到队列中.下一个任务将其取出并在Textblock中显示. 但是我似乎没有为UI获得正确的线程,尽管我认为我正确地使用了TaskScheduler. 感谢您的任何帮助! BlockingCollection<string> blockingCollection = new BlockingCollection<string>(); CancellationToken token = tokenSource.Token; TaskScheduler uiScheduler = TaskScheduler.FromCurrentSynchronizationContext(); Task task1 = new Task( (obj) => { for (int i = 0; i < 10; i++) { if (token.IsCancellationRequested) { TxtBlock2.Text = "Task cancel detected"; throw new OperationCanceledException(token); } else { string code = i.ToString() + "t" + AsyncHttpReq.get_source_WebRequest(uri); blockingCollection.Add(code); } } },TaskScheduler.Default); task1.ContinueWith(antecedents => { TxtBlock2.Text = "Signalling production end"; blockingCollection.CompleteAdding(); },uiScheduler); Task taskCP = new Task( (obj) => { while (!blockingCollection.IsCompleted) { string dlCode; if (blockingCollection.TryTake(out dlCode)) { //the calling thread cannot access this object because a different thread owns it. TxtBlock3.Text = dlCode; } } },uiScheduler); WindowsBase.dll!System.Windows.Threading.Dispatcher.VerifyAccess() + 0x4a bytes WindowsBase.dll!System.Windows.DependencyObject.SetValue(System.Windows.DependencyProperty dp,object value) + 0x19 bytes PresentationFramework.dll!System.Windows.Controls.TextBlock.Text.set(string value) + 0x24 bytes
System.InvalidOperationException was unhandled by user code Message=The calling thread cannot access this object because a different thread owns it. Source=WindowsBase StackTrace: at System.Windows.Threading.Dispatcher.VerifyAccess() at System.Windows.DependencyObject.SetValue(DependencyProperty dp,Object value) at System.Windows.Controls.TextBlock.set_Text(String value) at WpfRibbonApplication4.MainWindow.<>c__DisplayClass5.<Button1_Click>b__3(Object o) in C: ... WpfRibbonApplication4WpfRibbonApplication4MainWindow.xaml.cs:line 90 at System.Threading.Tasks.Task.InnerInvoke() at System.Threading.Tasks.Task.Execute() InnerException: 谢谢你的所有帮助. Brians代码: var task1 = new Task( (obj) => 为什么需要obj? private void Button1_Click(object sender,RoutedEventArgs e) { TaskScheduler uiTaskScheduler = TaskScheduler.FromCurrentSynchronizationContext(); CancellationToken token = cts.Token; Task task1 = Task.Factory.StartNew( () => { for (int i = 0; i < 10 ; i++) { token.ThrowIfCancellationRequested(); string code = i++.ToString() + "t" + AsyncHttpReq.get_source_WebRequest(uriDE); blockingCollection.Add(code); } },token,TaskCreationOptions.None,TaskScheduler.Default); task1.ContinueWith( (antecedents) => { if (token.IsCancellationRequested) { TxtBlock2.Text = "Task cancel detected"; } else { TxtBlock2.Text = "Signalling production end"; } blockingCollection.CompleteAdding(); },uiTaskScheduler); Task task2 = Task.Factory.StartNew( () => { while (!blockingCollection.IsCompleted) { string dlcode; if (blockingCollection.TryTake(out dlcode)) { TxtBlock3.Text = dlcode; } } },uiTaskScheduler); } 解决方法
好吧,实际上我只是再次查看你的代码,问题很简单:你正在使用带有状态对象的构造函数重载手动构建一个新的Task实例.没有构造函数重载需要TaskScheduler.
通常人们使用Task.Factory.StartNew,所以我甚至没有注意到你手动构建任务.手动构造Task实例时,指定调度程序以运行它们的正确方法是使用带有TaskScheduler实例的Start重载.所以在你的情况下你需要做: taskCP.Start(uiTaskScheduler); UPDATE 好的,现在你的更新代码的问题是你已经有效地安排了一个在UI(调度程序)线程上的任务,它坐在那里紧紧地循环阅读. 现在代码被重做了,很明显你不想在UI线程上安排task2.如果要将通知从那里推送到UI,您可以按照另一个答案的建议调用Dispatcher :: BeginInvoke,也可以使用循环内部的uiTaskScheduler启动新任务.调用Dispatcher :: BeginInvoke将减少开销,并且更清晰的代码恕我直言,所以我建议只是这样做. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |