一尘不染

异步编程和多线程有什么区别?

c#

我认为它们基本上是同一回事–编写在处理器之间(在具有2个以上处理器的机器上)将任务分割的程序。然后,我正在阅读this,它说:

异步方法旨在作为非阻塞操作。在等待的任务运行时,异步方法中的等待表达式不会阻塞当前线程。取而代之的是,表达式将方法的其余部分作为继续进行签名,并将控制权返回给异步方法的调用方。

async和await关键字不会导致创建其他线程。异步方法不需要多线程,因为异步方法不会在自己的线程上运行。该方法在当前同步上下文上运行,并且仅在该方法处于活动状态时才在线程上使用时间。您可以使用Task.Run将受CPU约束的工作移至后台线程,但是后台线程对仅等待结果可用的进程没有帮助。

我想知道是否有人可以帮我翻译成英文。似乎在异步性(是一个词?)和线程之间进行了区分,这意味着您可以拥有一个具有异步任务但没有多线程的程序。

现在,我了解了异步任务的想法,例如pg上的示例。乔恩·斯基特(Jon Skeet)的 C#深度 467 ,第三版

async void DisplayWebsiteLength ( object sender, EventArgs e )
{
    label.Text = "Fetching ...";
    using ( HttpClient client = new HttpClient() )
    {
        Task<string> task = client.GetStringAsync("http://csharpindepth.com");
        string text = await task;
        label.Text = text.Length.ToString();
    }
}

async关键字的意思是“ 这个功能,无论何时它被调用时,不会在这是需要的一切它的完成被称为它的呼叫后,上下文调用。”

换句话说,在某些任务的中间编写它

int x = 5; 
DisplayWebsiteLength();
double y = Math.Pow((double)x,2000.0);

,由于DisplayWebsiteLength()x或无关y,将导致DisplayWebsiteLength()“在后台”执行,例如

                processor 1                |      processor 2
-------------------------------------------------------------------
int x = 5;                                 |  DisplayWebsiteLength()
double y = Math.Pow((double)x,2000.0);     |

显然,这是一个愚蠢的例子,但是我是正确的,还是我完全感到困惑?

(此外,我对上面的函数的原因sender以及e从未使用过的方法感到困惑。)


阅读 894

收藏
2020-05-19

共1个答案

一尘不染

您的误解非常普遍。许多人被教导多线程和异步是同一回事,但事实并非如此。

类比通常会有所帮助。您在餐厅做饭。订购鸡蛋和吐司。

  • 同步:先煮鸡蛋,再煮吐司。
  • 异步单线程:开始煮鸡蛋并设置计时器。您开始烤面包,并设置一个计时器。他们俩都在做饭时,您要打扫厨房。计时器关闭后,您将鸡蛋和干面包从烤面包机中取出并送达。
  • 异步,多线程:您再雇用两名厨师,一名厨师煮鸡蛋,一名厨师烤面包。现在您需要协调厨师,以便他们在共享资源时不会在厨房互相冲突。而且你必须付钱。

现在,多线程仅仅是异步的一种有意义吗? 线程是关于工人的。
异步是关于任务的
。在多线程工作流中,您将任务分配给工作人员。在异步单线程工作流中,您可以看到一个任务图,其中某些任务取决于其他任务的结果。在每个任务完成时,给定刚完成的任务的结果,它将调用计划下一个可以运行的任务的代码。但是您(希望)仅需要一名工作人员即可执行所有任务,而不需要每个任务一名工作人员。

这将有助于认识到许多任务不是处理器约束的。对于与处理器相关的任务,合理的做法是雇用与处理器数量一样多的工作程序(线程),为每个工作程序分配一个任务,为每个工作程序分配一个处理器,让每个处理器除了计算结果外,不做其他任何事情尽快。但是对于没有在处理器上等待的任务,您根本不需要分配工作器。您只需要等待消息到达就可以得到结果,然后
在等待时执行其他操作即可 。当该消息到达时,您可以将已完成任务的继续安排为待办事项列表上的下一件事,以进行核对。

因此,让我们更详细地看一下乔恩的例子。怎么了?

  • 有人调用DisplayWebSiteLength。WHO?我们不在乎。
  • 它设置一个标签,创建一个客户端,并要求客户端获取一些东西。客户端返回一个对象,该对象表示获取某些内容的任务。该任务正在进行中。
  • 它在另一个线程上进行吗?可能不是。阅读Stephen的文章,了解为什么没有线程。
  • 现在我们等待任务。怎么了?我们检查任务在创建和等待之间是否完成。如果是,那么我们获取结果并继续运行。让我们假设它还没有完成。 我们将该方法的其余部分签名为该任务的继续并返回
  • 现在,控制权已返回给调用者。它有什么作用?无论它想要什么。
  • 现在,假设任务已完成。它是怎么做到的?也许它正在另一个线程上运行,或者也许我们刚刚返回的调用方允许它在当前线程上运行完成。无论如何,我们现在有一个完成的任务。
  • 完成的任务要求正确的线程(可能是 唯一的 线程)再次运行任务。
  • 控制权立即返回到我们刚刚在等待点离开的方法。现在有 一个结果,以便我们可以分配text和运行方法的其余部分。

就像我的类比。有人要求您提供文件。您发送了该文档的邮件,并继续进行其他工作。当它到达邮件中时,您会收到信号通知,并且当您感到喜欢时,就完成了其余的工作流程-
打开信封,支付邮寄费,无论如何。您无需雇用其他工人即可为您完成所有这些工作。

2020-05-19