当前位置:  开发笔记 > 编程语言 > 正文

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

如何解决《异步编程和多线程有什么区别?》经验,为你挑选了2个好方法。

我认为它们基本上是相同的 - 编写在处理器之间分割任务的程序(在具有2个以上处理器的机器上).然后我正在阅读https://msdn.microsoft.com/en-us/library/hh191443.aspx,其中说

异步方法旨在实现非阻塞操作.异步方法中的await表达式在等待的任务运行时不会阻止当前线程.相反,表达式将方法的其余部分作为延续进行注册,并将控制权返回给异步方法的调用者.

async和await关键字不会导致创建其他线程.异步方法不需要多线程,因为异步方法不能在自己的线程上运行.该方法在当前同步上下文上运行,并仅在方法处于活动状态时在线程上使用时间.您可以使用Task.Run将CPU绑定的工作移动到后台线程,但后台线程无助于正在等待结果可用的进程.

我想知道是否有人可以为我翻译成英文.它似乎区分了异步性(是一个单词?)和线程,并暗示你可以拥有一个具有异步任务但没有多线程的程序.

现在我理解异步任务的想法,例如pg上的示例.Jon Skeet的C#In Depth,第三版中的 467

async void DisplayWebsiteLength ( object sender, EventArgs e )
{
    label.Text = "Fetching ...";
    using ( HttpClient client = new HttpClient() )
    {
        Task 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);     |

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

(另外,我很困惑为什么sender并且e从未在上述功能的主体中使用过.)



1> Eric Lippert..:

你的误解非常普遍.许多人被教导多线程和异步是相同的,但事实并非如此.

类比通常有帮助.你在一家餐馆做饭.鸡蛋和烤面包的订单.

同步:你煮鸡蛋,然后你做烤面包.

异步,单线程:你开始煮鸡蛋并设置一个计时器.你开始吐司做饭,并设置一个计时器.虽然他们都在做饭,但你要清理厨房.当计时器响起时,你可以将鸡蛋从烤箱中取出,然后将烤面包机从烤面包机中取出并送达.

异步,多线程:你雇两个厨师,一个煮鸡蛋,一个煮烤面包.现在你遇到了协调厨师的问题,这样他们在共享资源时就不会在厨房里相互冲突.你必须付钱给他们.

现在,多线程只是一种异步才有意义吗? 线程是关于工人的; 异步是关于任务.在多线程工作流中,您可以将任务分配给工作人员.在异步单线程工作流程中,您有一个任务图表,其中某些任务取决于其他任务的结果; 当每个任务完成时,它会调用代码来调度可以运行的下一个任务,给定刚刚完成的任务的结果.但是你(希望)只需要一个工人来执行所有任务,而不是每个任务一个工人.

它将有助于意识到许多任务不受处理器约束.对于处理器绑定的任务,雇用与处理器一样多的工作者(线程),为每个工作者分配一个任务,为每个工作者分配一个处理器,让每个处理器完成除了计算结果之外的任何其他工作都是有意义的.尽快.但是对于没有在处理器上等待的任务,您根本不需要分配工作人员.您只是等待消息到达,结果可用并在您等待时执行其他操作.当该消息到达时,您可以安排继续完成的任务作为待办事项列表中的下一个要检查的事项.

让我们更详细地看一下Jon的例子.怎么了?

有人调用DisplayWebSiteLength.谁?我们不在乎.

它设置标签,创建客户端,并要求客户端获取某些内容.客户端返回一个表示抓取任务的对象.这项任务正在进行中.

它正在另一个线程上进行吗?可能不是.阅读斯蒂芬关于为什么没有线程的文章.

现在我们等待任务.怎么了?我们检查任务是否在我们创建它之间完成并等待它.如果是,那么我们获取结果并继续运行.我们假设它还没有完成. 我们将此方法的其余部分注册为该任务的继续并返回.

现在控制权已返回给调用者.它有什么作用?不管它想要什么.

现在假设任务完成.它是怎么做到的?也许它在另一个线程上运行,或者我们刚刚返回的调用者允许它在当前线程上运行完成.无论如何,我们现在已经完成了一项任务.

完成的任务要求正确的线程 - 再次,可能是唯一的线程 - 运行任务的继续.

控制立即返回到我们刚刚离开的方法中.现在有可用的结果,所以我们可以分配text和运行方法的其余部分.

就像我的比喻一样.有人问你要一份文件.您通过邮件发送文件,继续做其他工作.当它到达邮件时,您会收到信号,当您有这样的信息时,您会完成工作流程的其余部分 - 打开信封,支付运费,无论如何.您不需要聘请其他工作人员为您完成所有这些工作.


@ user5648283:再次,想想我的比喻.当有人要求你做鸡蛋和烤面包时,你会使用特殊的硬件 - 炉子和烤面包机 - 你可以在硬件正在工作时清洁厨房.如果有人要求您提供鸡蛋,烤面包以及对最后一张霍比特人电影的原创评论,您可以在鸡蛋和烤面包烹饪时写下您的评论,但您不需要使用硬件.
烤面包机是硬件.硬件不需要线程来为它服务; 磁盘和网卡以及远低于OS线程的级别运行.
@ user5648283:关于"重新安排代码"的问题,请考虑一下.假设你有一个具有收益率回报的方法P,以及一个方法Q,它对P的结果进行预测.逐步执行代码.你会看到我们运行一点Q然后一点点P然后一点点Q ...你明白这一点吗?**await主要是化装的收益率**.现在更清楚了吗?
@ user5648283:考虑任务的硬件是错误的.**任务只是一个对象,(1)表示某个值将来可用,(2)可以在该值可用时运行代码(在正确的线程上)**.任何单个任务如何在未来获得结果取决于它.有些人会使用像"磁盘"和"网卡"这样的特殊硬件来做到这一点; 有些人会使用像CPU这样的硬件.
@ShivprasadKoirala:**绝对不是真的**.如果你相信,那么你对异步**有一些非常错误的信念.C#中异步的全部意义在于它不会**创建​​一个线程.
@ShivprasadKoirala:*允许*异步方法使用线程池产生异步,但是*绝不是必需的!记住异步的目的是:**它是管理高延迟操作**。如果延迟的根源是因为CPU需要完成几秒钟的工作,则有必要将一个线程专用于该工作,而将CPU专用于该线程。但是,例如,如果高延迟操作正在等待网络或磁盘,那么给该线程分配线程是没有意义的!线程将进入睡眠状态!
我仍然对您上面的类比“与单线程异步”感到困惑。只有一位厨师。它代表一个线程。那么,烤面包机和煎锅对应什么呢?在我看来,烤面包机和煎锅也是工人,这又增加了2条线程。因此,它与单个线程的假设相矛盾。
@ShivprasadKoirala:另外,假设存在异步,因为我们正在等待将来在此线程上完成的*工作*.显然我们不需要创建一个新的线程,以便*这个线程*在将来*工作*,不仅仅是你需要雇佣一个厨师,以便*将来自己做午餐*.

2> StriplingWar..:

浏览器内的Javascript是没有线程的异步程序的一个很好的例子.

您不必担心同时触摸相同对象的多个代码片段:在允许任何其他javascript在页面上运行之前,每个函数都将完成运行.

但是,当执行类似AJAX请求的操作时,根本没有代码运行,因此其他javascript可以响应点击事件之类的事情,直到该请求返回并调用与之关联的回调.如果其中一个其他事件处理程序在AJAX请求返回时仍在运行,则在完成之前不会调用其处理程序.只有一个JavaScript"线程"在运行,即使你有可能有效地暂停你正在做的事情,直到你得到你需要的信息.

在C#应用程序中,只要您处理UI元素,就会发生同样的事情 - 当您在UI线程上时,您只能与UI元素进行交互.如果用户单击了一个按钮,并且您希望通过从磁盘读取大文件进行响应,那么没有经验的程序员可能会错误地在单击事件处理程序本身内读取该文件,这会导致应用程序"冻结"直到文件已完成加载,因为在释放该线程之前,不允许再响应任何更多点击,悬停或任何其他与UI相关的事件.

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

如果你考虑CPU在硬件/操作系统级别读取文件时所做的事情,它基本上会发出一条指令,将磁盘中的数据从内存读入内存,并在发出"中断"时触发操作系统.读完了.换句话说,从磁盘(或任何I/O)读取是一种固有的异步操作.等待I/O完成的线程的概念是库开发人员创建的抽象,以便更容易编程.这不是必需的.

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

async/await添加关键字之前,C#对于如何调用回调代码更为明显,因为这些回调采用与任务关联的委托形式.为了仍然为您提供使用该...Async()操作的好处,同时避免代码的复杂性,async/await抽象出这些代理的创建.但它们仍然存在于已编译的代码中.

因此,您可以让您的UI事件处理程序await进行I/O操作,释放UI线程以执行其他操作,并在读完文件后自动返回UI线程 - 无需创建一个新线程.


@oleksii:这在技术上是正确的,但我不打算进入,因为Web Workers API本身是异步的,并且不允许Web Workers直接影响它们被调用的网页上的javascript值或DOM来自,这意味着这个答案的关键第二段仍然适用.从程序员的角度来看,调用Web Worker和调用AJAX请求之间没有什么区别.
推荐阅读
地之南_816
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有