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

dev*_*ife 12 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());


    }
}
Run Code Online (Sandbox Code Playgroud)

执行此代码我得到无处不在的异常:

跨线程操作无效:控制'textBox1'从其创建的线程以外的线程访问.

Dre*_*rsh 16

是的,您可以显式调用BeginInvoke要与之通信的Window/Control.在你的情况下,这将是这样的:

this.textBox.BeginInvoke(new Action(() =>
{
   this.textBox.Text = "From child task.";
}));
Run Code Online (Sandbox Code Playgroud)


svi*_*ick 5

你正在传递TaskScheduleras 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";
    });
Run Code Online (Sandbox Code Playgroud)

当然,使用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;
}
Run Code Online (Sandbox Code Playgroud)

你不能创建async构造函数,但是你可以从中运行一个async方法."正在工作"不会在UI线程上运行,因为它是通过显式启动的Task.Run().但由于StartAsync()直接调用,它在UI线程上执行.