asynchronous编程和multithreading有什么区别?

我认为它们基本上是一样的 – 编写在处理器之间分配任务的程序(在具有2+处理器的机器上)。 然后,我正在阅读https://msdn.microsoft.com/en-us/library/hh191443.aspx ,它说

asynchronous方法旨在成为非阻塞操作。 asynchronous方法中的awaitexpression式在等待执行的任务正在运行时不会阻塞当前线程。 expression式将该方法的其余部分注册为continuation,并将控制返回给asynchronous方法的调用者。

asynchronous和等待关键字不会导致创build额外的线程。 asynchronous方法不需要multithreading,因为asynchronous方法不能在自己的线程上运行。 该方法在当前的同步上下文上运行,并且仅在该方法处于活动状态时才在该线程上使用时间。 您可以使用Task.Run将CPU绑定的工作移至后台线程,但后台线程无助于只等待结果可用的进程。

我想知道是否有人能为我翻译成英文。 它似乎在asynchronous性(是一个单词?)和线程之间作了区分,并暗示您可以拥有一个具有asynchronous任务但没有multithreading的程序。

现在我明白了asynchronous任务的概念,例如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()xy无关,会导致DisplayWebsiteLength()在后台执行,就像

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

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

(另外,我很困惑,为什么sendere在上面的函数中没有被使用过。)

你的误解是非常普遍的。 许多人被教导说multithreading和asynchronous是一回事,但事实并非如此。

类比通常有帮助。 你正在一家餐馆做饭。 一个订单进来鸡蛋和烤面包。

  • 同步:你煮鸡蛋,然后你做烤面包。
  • asynchronous,单线程:你开始煮鸡蛋并设置一个计时器。 你开始敬酒烹饪,并设置一个计时器。 当他们都在做饭的时候,你打扫厨房。 当定时器熄灭时,将鸡蛋从烤箱中取出,烤面包片,然后送达。
  • asynchronous,multithreading:你再雇用两个厨师,一个煮鸡蛋,一个煮面包。 现在你有协调厨师的问题,这样在厨房分享资源时就不会相互冲突。 而你必须付钱。

multithreading只是一种不同步现在是否有意义? 线程是关于工人的; asynchronous是关于任务的 。 在multithreading工作stream程中,您将任务分配给工作人员。 在asynchronous单线程工作stream中,有一些任务图,其中一些任务取决于其他任务的结果; 当每个任务完成时,调用执行下一个任务的代码,给定刚完成的任务的结果。 但是你(希望)只需要一个工人来完成所有的任务,而不是每个任务的一个工人。

这将有助于认识到许多任务不受处理器限制。 对于处理器绑定的任务来说,雇用与处理器一样多的工作者(线程)是有意义的,为每个工作者分配一个任务,为每个工作者分配一个处理器,并让每个处理器完成除了计算结果尽可能迅速。 但对于没有在处理器上等待的任务,根本不需要分配工作人员。 你只要等待消息到达,结果是可用的, 并在等待时做别的事情 。 当这个消息到达时,你可以安排完成任务的继续作为你的待办事项列表中的下一件事情来检查。

所以让我们来看看Jon的例子。 怎么了?

  • 有人调用DisplayWebSiteLength。 谁? 我们不在乎。
  • 它设置一个标签,创build一个客户端,并要求客户端获取一些东西。 客户端返回一个代表取出任务的对象。 这项任务正在进行中。
  • 在另一个线程中是否正在进行? 可能不会。 阅读斯蒂芬的文章 ,为什么没有线程。
  • 现在我们等待这个任务。 怎么了? 我们检查一下,看看这个任务是否在我们创build它的时间和我们等待的时间之间完成。 如果是,那么我们取得结果并继续运行。 假设它还没有完成。 我们将这个方法的其余部分注册为这个任务的继续并返回
  • 现在控制已经返回给调用者。 它有什么作用? 无论如何。
  • 现在假设任务完成。 这是怎么做到的? 也许它在另一个线程上运行,或者我们刚刚返回的调用者允许它在当前线程上运行完成。 无论如何,我们现在有一个完成的任务。
  • 完成的任务询问正确的线程 – 再次,可能是唯一的线程 – 运行任务的继续。
  • 控制立即返回到我们刚刚在等待的地方留下的方法。 现在有一个结果可用,所以我们可以分配text并运行该方法的其余部分。

就像我的比喻。 有人问你一个文件。 你在邮件中发送文件,并继续做其他工作。 当邮件到达邮件时发出信号,当你感觉到这种情况时,你可以完成工作stream程的其余部分 – 打开信封,支付邮费。 你不需要雇用另一名工人为你做这一切。

浏览器中的Javascript是一个没有线程的asynchronous程序的好例子。

您不必担心同时触及相同对象的多段代码:每个函数都将在其他任何javascript允许在页面上运行之前结束运行。

但是,当做类似AJAX请求的代码时,根本没有代码运行,所以其他javascript可以响应点击事件,直到请求返回并调用与之相关的callback。 如果其中一个事件处理程序在AJAX请求返回时仍在运行,那么它的处理程序在完成之前不会被调用。 只有一个JavaScript“线程”正在运行,即使可以有效地暂停正在执行的操作,直到获得所需信息为止。

在C#应用程序中,每次处理UI元素时都会发生同样的事情 – 在UI线程中,只允许与UI元素进行交互。 如果用户点击一个button,并且想要通过读取磁盘上的大文件进行响应,那么缺乏经验的程序员可能会在点击事件处理程序本身中读取文件,从而导致应用程序“冻结”文件完成加载,因为它不被允许响应任何更多的点击,hover,或任何其他UI相关的事件,直到该线程被释放。

程序员可以用来避免这个问题的一个select是创build一个新的线程来加载文件,然后告诉该线程的代码,当文件被加载时,需要再次在UI线程上运行剩余的代码,以便它可以更新UI元素基于它在文件中find的内容。 直到最近,这种方法非常受欢迎,因为C#库和语言变得简单,但是从根本上讲它比它更复杂。

如果考虑CPU在硬件/操作系统级别读取文件时正在做什么,基本上就是发出一条指令,从磁盘读取数据到内存中,当用“中断”打开操作系统时读取完成。 换句话说,从磁盘(或任何I / O真正)读取是一个固有的asynchronous操作。 等待I / O完成的线程的概念是库开发人员创build的一个抽象,以使其更容易编程。 这不是必需的。

现在,大多数.NET中的I / O操作都有一个相应的...Async()方法,您可以调用它,它几乎立即返回一个Task 。 您可以将callback添加到此Task以指定在asynchronous操作完成时要运行的代码。 您还可以指定要运行哪个代码的线程,并且可以提供asynchronous操作可以检查的令牌,以查看是否决定取消asynchronous任务,从而使其有机会快速停止其工作和优雅。

在添加async/await关键字之前,C#对于callback代码的调用方式要明显得多,因为这些callback是以与任务相关的代理的forms出现的。 为了仍然给你使用...Async()操作的好处,同时避免代码中的复杂性, async/await提取这些代表的创build。 但是他们仍然在编译的代码中。

所以你可以让你的UI事件处理程序await一个I / O操作,释放UI线程去做其他事情,一旦你完成了读取文件,或多或less的自动返回到UI线程 – 从来没有创build一个新的线程。