我有一个简单的小winforms应用程序,通过TPL任务在另一个线程上执行长时间运行的进程。在这个漫长的运行过程中,我想更新UI(进度条或其他内容)。有没有办法在不需要的情况下执行此操作.ContinueWith()?
public partial class Form1 : Form
{
private Task _childTask;
public Form1()
{
InitializeComponent();
Task.Factory.StartNew(() =>
{
// Do some work
Thread.Sleep(1000);
// Update the UI
_childTask.Start();
// Do more work
Thread.Sleep(1000);
});
_childTask = new Task((antecedent) =>
{
Thread.Sleep(2000);
textBox1.Text = "From child task";
}, TaskScheduler.FromCurrentSynchronizationContext());
}
}
执行此代码我得到了无处不在的异常:
跨线程操作无效:控制'textBox1'从其创建的线程以外的线程访问。
答案 0 :(得分:13)
是的,您可以在要与之通信的Window / Control上显式调用BeginInvoke
。在你的情况下,这将是这样的:
this.textBox.BeginInvoke(new Action(() =>
{
this.textBox.Text = "From child task.";
}));
答案 1 :(得分:5)
您将TaskScheduler
传递为state
(或antecedent
,就像您所说的那样)。这没有多大意义。
我不确定您到底想要做什么,但是当您启动TaskScheduler
时需要指定Task
,而不是在创建它时。此外,似乎childTask
不一定是字段:
var scheduler = TaskScheduler.FromCurrentSynchronizationContext();
Task childTask = null;
Task.Factory.StartNew(
() =>
{
Thread.Sleep(1000);
childTask.Start(scheduler);
Thread.Sleep(1000);
});
childTask = new Task(
() =>
{
Thread.Sleep(2000);
textBox1.Text = "From child task";
});
当然,使用C#5和await
:
public Form1()
{
InitializeComponent();
StartAsync();
}
private async void StartAsync()
{
// do some work
await Task.Run(() => { Thread.Sleep(1000); });
// start more work
var moreWork = Task.Run(() => { Thread.Sleep(1000); });
// update the UI, based on data from “some work”
textBox1.Text = "From async method";
// wait until “more work” finishes
await moreWork;
}
您无法创建async
构造函数,但可以从中运行async
方法。 “做工作”不在UI线程上运行,因为它是通过Task.Run()
显式启动的。但是由于StartAsync()
是直接调用的,所以它在UI线程上执行。