为什么Await之后的代码不能在UI线程上运行?

use*_*744 1 c# async-await

我过去使用过代理来更新当前的UI(例如文本框),其中包含在我的线程中处理的值.我是在假设async并await为你照顾这个.

当我运行下面的代码时 - 按下按钮时,启动一个新的任务,每100ms计数100次.计数应显示在UI上,但我得到一个targetInvocationException:

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
        textBox1.Text = "Press button";
    }

    private async void button1_Click(object sender, EventArgs e)
    {
         await Task1();
    }

    private Task Task1()
    {
        return Task.Factory.StartNew(() => CountMethod());
    }

    private void CountMethod()
    {
        for (int i = 0; i < 100; i++)
        {
            Task.Delay(100).Wait();
            //Console.WriteLine(i.ToString());
            textBox1.Text = i.ToString();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

Yac*_*sad 9

问题是您正在使用线程池线程来访问UI.您正在使用该Task.Factory.StartNew方法执行此操作.

以下是如何解决它:

private async Task Task1()
{
    for (int i = 0; i < 100; i++)
    {
        await Task.Delay(100);

        textBox1.Text = i.ToString();
    }
}
Run Code Online (Sandbox Code Playgroud)

await将捕获当前,SynchronizationContext以便在异步等待完成后,后续代码在UI线程上执行.这是真的,因为awaitUI线程调用了(在我的例子中).

在当前代码中,await在线程池线程上调用.在这种情况下,当前SynchronizationContext为空,因此异步等待之后的代码也将在线程池线程上执行.

  • @ user3363744:不,`async`与多线程相反 - 它使用*less*threads而不是*more*.如果您需要移出UI线程,那么您将使用线程池(`Task.Run`,而不是`StartNew`). (5认同)