异步任务 - CPU 上实际发生了什么?

isp*_*iro 1 .net c# multithreading asynchronous async-await

Task在提出这个问题后,我一直在阅读有关s 的内容,发现我完全误解了这个概念。此处此处的最佳答案等答案 解释了这个想法,但我仍然不明白。\n所以我提出了一个非常具体的问题:CPU 上实际发生了什么执行任务时

\n\n

这是我在阅读后了解到的:任务将与调用者共享 CPU 时间(假设调用者是“UI”),因此如果它是 CPU 密集型的,那么它会变慢下用户界面。如果任务不是CPU密集型的 - 它将“在后台”运行。在测试之前似乎足够清楚 \xe2\x80\xa6\xe2\x80\xa6 。下面的代码应该允许用户单击按钮,然后交替显示“已显示”和“按钮”。但实际上:表单完全繁忙(无法进行用户输入),直到“已显示”全部显示为止。

\n\n
public Form1()\n{\n    InitializeComponent();\n    Shown += Form1_Shown;\n}\n\nprivate async void Form1_Shown(object sender, EventArgs e)\n{\n    await Doit("Shown");\n}\n\nprivate async Task Doit(string s)\n{\n    WebClient client = new WebClient();\n    for (int i = 0; i < 10; i++)\n    {\n        client.DownloadData(uri);//This is here in order to delay the Text writing without much CPU use.\n        textBox1.Text += s + "\\r\\n";\n        this.Update();//textBox1.\n    }\n}\n\nprivate async void button1_Click(object sender, EventArgs e)\n{\n    await Doit("Button");\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

有人可以告诉我执行任务时CPU上实际发生了什么(例如“当UI不使用CPU时,任务使用它,除了\xe2\x80\xa6等”)?

\n

Ste*_*ary 5

理解这一点的关键是,有两种任务- 一种执行代码(我称之为委托任务),另一种代表未来事件(我称之为 Promise 任务)。这两个任务完全不同,尽管它们都由 .NET 中的实例表示Task我的博客上有一些漂亮的图片,可能有助于理解这些类型的任务有何不同。

Task.Run委托任务是由朋友创建的任务。它们在线程池(或者TaskScheduler如果您使用的是另一个线程池TaskFactory)上执行代码。大多数“任务并行库”文档都涉及委托任务。它们用于将 CPU 密集型算法分散到多个 CPU 上,或将 CPU 密集型工作从 UI 线程中推送出去。

TaskCompletionSource<T>Promise 任务是由和 朋友(包括)创建的任务async。这些是用于异步编程的,并且非常适合 I/O 密集型代码。

请注意,您的示例代码将导致编译器警告,大意是您的“异步”方法Doit实际上不是异步的,而是同步的。所以现在的情况是,它会同步调用DownloadData,阻塞 UI 线程,直到下载完成,然后更新文本框,最后返回一个已经完成的任务。

要使其异步,您必须使用await

private async Task Doit(string s)
{
  WebClient client = new WebClient();
  for (int i = 0; i < 10; i++)
  {
    await client.DownloadDataTaskAsync(uri);
    textBox1.Text += s + "\r\n";
    this.Update();//textBox1.
  }
}
Run Code Online (Sandbox Code Playgroud)

现在,当它点击 时,它会返回一个未完成的任务await,这允许 UI 线程返回到其消息处理循环。下载完成后,此方法的其余部分将作为消息排队到 UI 线程,并且当它有时间时将继续执行该方法。当该Doit方法完成时,它之前返回的任务将完成。

因此,方法返回的任务async在逻辑上代表该方法。任务本身是一个 Promise Task,而不是一个 Delegate Task,并且实际上并不“执行”。该方法被分成多个部分(在每个await点)并分块执行,但任务本身不在任何地方执行。

为了进一步阅读,我有一篇关于如何asyncawait实际工作(以及它们如何调度方法块)的博客文章,以及另一篇关于为什么异步 I/O 任务不需要阻塞线程的博客文章。