如何从WinForms中的子任务更新UI

时间:2012-03-12 19:10:08

标签: winforms task-parallel-library

我有一个简单的小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'从其创建的线程以外的线程访问。

2 个答案:

答案 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线程上执行。