Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Multithreading 为什么异步被认为比多线程性能更好?_Multithreading_Performance_Asynchronous - Fatal编程技术网

Multithreading 为什么异步被认为比多线程性能更好?

Multithreading 为什么异步被认为比多线程性能更好?,multithreading,performance,asynchronous,Multithreading,Performance,Asynchronous,我了解异步编程和多线程编程,并且我已经完成了这两项工作,可以轻松地完成它们。然而有一件事仍然困扰着我:为什么人们普遍认为异步比多线程性能更好?(补充:我说的是两种方法都是可行的,你可以做出选择) 乍一看,原因似乎很清楚——线程更少,操作系统调度程序的工作量更少,堆栈空间浪费的内存更少。但是我觉得这些论点站不住脚。让我们分别来看看它们: 操作系统调度器的工作量更少。没错,但这是否意味着总工作量减少了?仍然有N个任务并行运行,必须有人在它们之间切换。在我看来,我们只是从操作系统内核开始工作,并开始在

我了解异步编程和多线程编程,并且我已经完成了这两项工作,可以轻松地完成它们。然而有一件事仍然困扰着我:为什么人们普遍认为异步比多线程性能更好?(补充:我说的是两种方法都是可行的,你可以做出选择)

乍一看,原因似乎很清楚——线程更少,操作系统调度程序的工作量更少,堆栈空间浪费的内存更少。但是我觉得这些论点站不住脚。让我们分别来看看它们:

  • 操作系统调度器的工作量更少。没错,但这是否意味着总工作量减少了?仍然有N个任务并行运行,必须有人在它们之间切换。在我看来,我们只是从操作系统内核开始工作,并开始在我们自己的用户代码中进行。但需要完成的工作量并没有因此而改变。那么,效率从何而来
  • 减少了堆栈空间的内存浪费。还是这样?
    • 首先,我不知道其他操作系统的情况,但至少在Windows中,线程的堆栈空间不是一次性提交的。有保留的虚拟内存地址,但实际内存仅在需要时提交
    • 即使它被提交了,也没什么大不了的,因为仅仅分配内存并不会降低程序的速度。除非你的内存用完了,现代计算机有足够的内存来存储上千个堆栈,尤其是服务器
    • 即使堆栈确实被提交并最终导致内存不足,大多数堆栈也只会在开始时使用一点(如果您的程序遇到堆栈溢出,那么您需要担心更大的问题)。这意味着不管怎样,都有可能将其中的大部分内容分页出来
    • 大内存使用的真正问题是CPU缓存被大量破坏。当你需要的地方到处都有大量的数据,而CPU缓存跟不上这些数据,需要一次又一次地从主RAM获取数据时,事情就会变得很慢。但是异步编程在这方面没有任何帮助。如果有的话,它会主动使用更多内存。我们现在不再使用精简堆栈框架,而是在堆上为每个堆栈框架分配单独的
      Task
      对象,这些堆栈框架包含状态和局部变量、回调引用以及所有内容。此外,它在整个地址空间中都是碎片,这给CPU缓存带来了更多的麻烦,因为预取是无用的
  • 所以。。。我错过了房间里的哪一头大象?

    (在这个回答中,我将谈论.NET,因为它是
    async/await
    推出的第一项技术。)

    我们使用线程并行化CPU绑定的任务,使用异步IO并行化IO绑定的任务

    CPU方面:
    我们都知道每个任务一个线程是错误的。我们不需要太多线程,因为上下文切换将冻结整个系统。我们不要太少,因为我们希望任务尽快完成。当然,我们正在寻找某种线程池。
    ThreadPool是在
    任务
    之前调度异步任务的默认方式。但是线程池有一个棘手的问题——很难知道异步何时完成,以及异步结果或异常是什么。
    然后是
    任务
    。任务不仅在线程池上调度委托,而且在任务完成时,您还可以获得结果或异常,并使用
    task.ContinueWith
    从中继续工作

    IO方面:
    我们都知道每个连接的线程是一件坏事。如果我们想让优化后的服务器每秒处理数百万个请求,我们不能只为每个新连接生成一个新线程。我们的系统将在上下文切换时窒息。所以我们使用异步IO。在
    Task
    之前的时代,我们使用了
    BeginRead/EndRead
    BeginWrite/EndWrite
    ,这两种方法都很容易出错,而且使用起来很痛苦-我们不得不使用糟糕的事件驱动编程模式
    然后是
    任务
    。我们可以启动异步IO操作,并通过
    任务接收结果或异常。继续执行
    。它使异步IO更易于使用

    Task
    是连接异步CPU任务和异步IO任务的粘合剂。通过一个接口,我们可以调度一个异步函数,并通过
    Task.ContinueWith
    获得结果。难怪用
    Task
    s编程变得如此流行

    任务。ContinueWith
    非常不可读和不可写。

    基本上,将一个任务链接到一个任务到一个任务到一个任务。。。头痛。正如Node.js开发人员抱怨的那样(即使在js
    async/await
    中,将来也会标准化)。async/Wait在这里进行救援。基本上,C#编译器在幕后完成了一个简洁的巫术。简而言之,它接受
    await
    之后的所有内容,并使用状态机对其进行打包,当
    await
    之前的所有内容完成时,将调用状态机。编译器接受同步代码(用
    async
    /
    wait
    注释),并为您执行
    连续操作

    那么,为什么要使用
    异步
    /
    等待
    +
    任务
    而不是多线程代码呢

  • async
    /
    await
    是获取异步结果或异常的最简单方法。(相信我,我用C++编写了异步代码,C语言,java和JavaScript,<代码>异步代码>代码>等待< /COD>是该领域的天堂。
  • async
    /
    await
    可用于CPU绑定任务和IO绑定任务。两个不同但相似字段的相同接口
  • 如果您想要异步IO,那么