diff --git a/AdvFinML-Standard-Bars.html b/AdvFinML-Standard-Bars.html index a2da392c..391a7085 100755 --- a/AdvFinML-Standard-Bars.html +++ b/AdvFinML-Standard-Bars.html @@ -134,6 +134,36 @@

Related Posts:

  • DataFrame rolling apply 多列 return 多列
  • +
    +
    +

    Comments

    + +
    + + + comments powered by Disqus + +
    @@ -169,7 +199,7 @@

    Social

  • Android
  • -
  • +
  • async
  • @@ -199,7 +229,7 @@

    Social

  • Hifi
  • -
  • +
  • javascript
  • @@ -223,7 +253,7 @@

    Social

  • monkey
  • -
  • +
  • multithread
  • @@ -238,7 +268,7 @@

    Social

  • Pandas
  • -
  • +
  • programming
  • @@ -326,6 +356,21 @@

    Lin + + + diff --git a/action-of-bad-ml-model.html b/action-of-bad-ml-model.html index 3f583947..6f84abda 100755 --- a/action-of-bad-ml-model.html +++ b/action-of-bad-ml-model.html @@ -158,6 +158,36 @@

    Related Posts:

  • AdvFinML - Standard Bars
  • +
    +
    +

    Comments

    + +
    + + + comments powered by Disqus + +
    @@ -193,7 +223,7 @@

    Social

  • Android
  • -
  • +
  • async
  • @@ -223,7 +253,7 @@

    Social

  • Hifi
  • -
  • +
  • javascript
  • @@ -247,7 +277,7 @@

    Social

  • monkey
  • -
  • +
  • multithread
  • @@ -262,7 +292,7 @@

    Social

  • Pandas
  • -
  • +
  • programming
  • @@ -350,6 +380,21 @@

    Lin + + + diff --git a/adb-support-monkey.html b/adb-support-monkey.html index c086efac..5e8e5ec0 100755 --- a/adb-support-monkey.html +++ b/adb-support-monkey.html @@ -576,6 +576,36 @@

    Related Posts:

  • 当技术遇到内容
  • +
    +
    +

    Comments

    + +
    + + + comments powered by Disqus + +
    @@ -611,7 +641,7 @@

    Social

  • Android
  • -
  • +
  • async
  • @@ -641,7 +671,7 @@

    Social

  • Hifi
  • -
  • +
  • javascript
  • @@ -665,7 +695,7 @@

    Social

  • monkey
  • -
  • +
  • multithread
  • @@ -680,7 +710,7 @@

    Social

  • Pandas
  • -
  • +
  • programming
  • @@ -768,6 +798,21 @@

    Lin + + + diff --git a/android-adb-talk.html b/android-adb-talk.html index 77fb3df2..0600e566 100755 --- a/android-adb-talk.html +++ b/android-adb-talk.html @@ -382,6 +382,36 @@

    Related Posts:

  • 当技术遇到内容
  • +
    +
    +

    Comments

    + +
    + + + comments powered by Disqus + +
    @@ -417,7 +447,7 @@

    Social

  • Android
  • -
  • +
  • async
  • @@ -447,7 +477,7 @@

    Social

  • Hifi
  • -
  • +
  • javascript
  • @@ -471,7 +501,7 @@

    Social

  • monkey
  • -
  • +
  • multithread
  • @@ -486,7 +516,7 @@

    Social

  • Pandas
  • -
  • +
  • programming
  • @@ -574,6 +604,21 @@

    Lin + + + diff --git a/archives.html b/archives.html index 778de6e5..728a2153 100755 --- a/archives.html +++ b/archives.html @@ -87,7 +87,11 @@

    Archives for TechLoveDeath

    - Async and multithread programming - 1 Async in JavaScript + 异步和多线程自编程 - 1 JavaScript中的异步 +

    +

    + + Async&Multithrad - 1 Async - 1 Async in JavaScript

    @@ -399,7 +403,7 @@

    Social

  • Android
  • -
  • +
  • async
  • @@ -429,7 +433,7 @@

    Social

  • Hifi
  • -
  • +
  • javascript
  • @@ -453,7 +457,7 @@

    Social

  • monkey
  • -
  • +
  • multithread
  • @@ -468,7 +472,7 @@

    Social

  • Pandas
  • -
  • +
  • programming
  • @@ -556,6 +560,21 @@

    Lin + + + diff --git a/async-and-multithread-programming-1-async-cn.html b/async-and-multithread-programming-1-async-cn.html index 2a8cf627..21e70a6d 100755 --- a/async-and-multithread-programming-1-async-cn.html +++ b/async-and-multithread-programming-1-async-cn.html @@ -139,38 +139,33 @@

    整个系列的目录

  • 5.多线程与异步编程的关系 (主要讲GoRotine,分析源码,看一下rust有没有改进)
  • 6.GPU并行编程
  • -

    introduction

    -

    background

    -

    我们这个系列主要是讲异步和多线程。异步和多线程本质上来讲都是为了充分的利用计算资源。

    -

    当CPU单核主频的发展速度达到瓶颈之后,往多核CPU发展。同时GPU的并行运算给LLM的发展有着极大的促进作用。当然我这一个系列主要是讲CPU,这里我只是想表明并行计算这个概念在现在这个时代非常重要。

    +

    介绍

    +

    背景(异步&多线程)

    +

    这个系列主要是讲异步和多线程。异步和多线程本质上来讲都是为了充分的利用计算资源。

    +

    当CPU单核主频的发展速度达到瓶颈之后,往多核CPU发展。最近一些年GPU的并行矩阵运算给尝试学习和LLM的发展有着极大的促进作用。我这一个系列主要是讲CPU,暂时并不涉及GPU。目的主要是介绍一下异步和多线程编程,以及他们之间的关系。

    那么异步编程是指什么?它和多线程是什么关系?多线程与硬件的关系是什么?异步编程&多线程编程与并行计算又有什么关系?我们将通过这个系列搞清楚这些问题。

    -

    我将会不定期的更新这个系列的文章

    +

    (如果我发现了一些错误或者一些新的观点,我会更新已经发布过的文章内容,或者变更这个系列文章的结构)

    in this article

    在这篇文章里面我会介绍:

    1. 什么是异步编程?为什么需要它?
    2. JavaScript中的异步编程 a. 回调函数 - b. 事件循环(源码) - b. promise - c. await
    3. -
    4. 怎么判断IO操作有没有结束?(源码) - a. 网络操作 - b. 文件操作 - c. 有没有浪费资源呢? - 对于cpu来讲是没有的,因为就算
    5. + b. 事件循环(源码) +
    6. 下一篇文章

    实验环境

    OS: Windows 11 21H2 22000.2538

    Node.js: 20.13.1

    什么是异步编程?

    -

    异步的反面是同步编程。

    +

    什么是异步

    +

    异步的反面是同步。我们先看一个生活中的例子:我去邮局寄信找朋友借钱,然后我收到支票后把它存到银行。我可以用同步异步的方式来实现:

    Basic Introduction to Async and Sync

    Figure 1. Basic Introduction to Async and Sync

    -

    我用Node.js写了一个示例程序,来看看执行结果MailDemo

    +

    我用Node.js写了一个示例程序,来看看执行结果点击查看源码 MailDemo

    Synchronized Mailing Demo

    Figure 2. Synchronized Mailing Demo @@ -181,10 +176,10 @@

    什么是异步编程?

    Figure 3. Synchronized Mailing Demo

    -

    所以我们能看到同步和异步最大的差别就是我(寄件人)在把邮件给邮局后是否在邮局等待朋友的回信。

    -

    从这个实例可以看出,很明显的在异步的邮寄过程当中,这个安排更合理。在同步的邮寄程序中有个看似非常奇怪的问题:我为什么把邮件给邮局后,我为什么还要在邮局傻傻的等? 我明明可以去吃饭,逛街,看电影做很多事情。朋友回复邮件了,邮局可以打电话通知我,我再去取啊。

    -

    所以在邮局等待是一个不合理的操作,有一个词叫阻塞经常和同步编程一起出现。阻塞意味着主人翁没有做任何事情的在等待一个结果。发生在现实生活中不合理,同样在程序中也不合理,所以异步编程会解决这个问题。

    -

    在工业界有个领域叫统筹规划,简单来讲就是就是让资源(包括机器、人力等)保持高效运转,从而在相同的时间内完成更多的任务。(在这里我希望大家可以不要像机器一样一直运转,在享受生活的同时,也能赚到很多钱。是的,统筹规划不应该应该到人身上。)

    +

    所以我们能看到同步和异步最大的差别就是我(寄件人)在把邮件给邮局后是否在邮局等待朋友的回信还是可以去做其他的事情

    +

    从这个实例可以看出,很明显的在异步的邮寄过程当中,这个安排更合理。在同步的邮寄程序中有个看似非常奇怪的问题:我为什么把邮件给邮局后,我为什么还要在邮局傻傻的等? 我明明可以去吃饭,逛街,看电影做很多事情。朋友回复邮件了,邮局可以打电话通知我,我再去取。

    +

    所以在邮局等待是一个不合理的操作。有一个词叫阻塞经常和同步编程一起出现,阻塞意味着主人翁没有做任何事情的在等待一个结果。发生在现实生活中不合理,同样在程序中也不合理,所以异步编程会解决这个问题。

    +

    在工业界有个领域叫统筹规划,简单来讲就是就是让资源(包括机器、人力等)保持高效运转,从而在相同的时间内完成更多的任务。(在这里我希望大家可以不要像机器一样一直运转,在享受生活的同时,也能赚到很多钱。是的,统筹规划不应该应该到人身上。)

    回到编程我们看一看异步同步的时序图。

    Synchronized Mailing Sequence Diagram

    @@ -196,64 +191,67 @@

    什么是异步编程?

    Figure 5. Asynchronized Mailing Sequence Diagram

    -

    寄信这个活动有什么特点呢? - 1. 是邮局来寄,不需要我 +

    通过这两张时序图可以更加清晰的看到,异步更合理。因为在邮局寄售的过程中我可以同时回家吃饭、逛街做很多事情,没有必要在邮局等候。

    +

    那寄信这个活动有什么特点呢? + 1. 是别人(邮局)来寄,不需要我自己 2. 等待回信需要比较长的时间

    -

    在编程里面我们可以想到类似的操作:文件读写,网络请求(TCP,HTTP,DNS) 等等。

    +

    异步编程

    +

    在编程里面有一些和寄信一样类似的操作,如:文件读写,网络请求(TCP,HTTP,DNS) 等等。

      -
    • 文件读写:磁盘控制器去帮我们找到我们想要的内容
    • -
    • 网络请求:服务器在处理请求
    • +
    • 文件读写:磁盘控制器去帮我们找到我们想要的内容
    • +
    • 网络请求:服务器在处理请求
    • +
    • Timer也是,Timer就是不需要等待任何返回,就是单纯的等待。
    -

    Timer也是,Timer就是不需要等待任何返回,就是单纯的等待。

    -

    异步编程就是让主线程在等待时去做其他的事情。

    +

    磁盘控制器,服务器这些就是第三方,相当于之前例子中的邮局。处理这些任务的时间长短关键在于他们,我是无能为力的。

    +

    总结一下:异步编程就是让主线程在等待某些任务在第三方处理时去做其他的任务

    JavaScript中的异步编程

    我为什么要讲JavaScript中的异步编程?

    -

    因为JavaScript是设计成单线程的,在Browser里面是事件响应型的模型,而且网络请求(年纪大点的人应该听过Ajax)又多,所以一旦有点阻塞,Browser来不及处理用户的Click和界面的渲染,整个UI就会处于无响应的状态,用户体验就会极差。

    -

    为什么不设计成多线程的?处理多线程极其的麻烦,会让编程者的头发掉光。相比之下异步模型会编写起来会简单很多。

    +

    因为JavaScript最早是设计成单线程的,在Browser里面是基于事件响应型的模型,而且网络请求(年纪大点的人应该听过Ajax)又多,所以一旦有点阻塞,Browser来不及处理用户的Click和界面的渲染,整个UI就会处于无响应的状态,用户体验就会极差。(JavaScript浏览器环境除了主线程,会有一些其他的辅助线程)

    +

    为什么不设计成多线程的?处理多线程极其的麻烦,会让编程者掉更多的头发。相比之下异步模型会编写起来会比多线程编程简单很多。

    比如:一个Web page在Browser里面请求一个REST API(HTTP): Mail, 需要等待2s才能回复数据。 -如果是同步:在这2s内,用户点击页面是没有响应的,因为主线程在这2s内的任务是等待Mail的返回,无法做其他的事情。 -如果是异步:这2s内主线程是空闲的,完全可以处理用户的任何请求,而不会出现UI无响应的情况。那如果在这2s内,如:1.2秒,Mail突然返回了数据,会打断其他的任务吗?不会。因为1.2后接受到Mail的返回时JS会把Mail的回调函数插入事件循环,等待当前任务执行完毕后才会从事件循环中取出Mail的回调函数并执行相应的处理任务。

    -

    我们刚刚碰到了两个新的名词:事件循环回调函数。我们来解释一下JavaScript中是怎么用他们来实现异步编程的。

    +- 如果是同步:在这2s内,用户点击页面是没有响应的,因为主线程在这2s内只能等待Mail的返回,无法做其他的事情。 +- 如果是异步:这2s内主线程是空闲的,完全可以处理用户的任何请求和渲染页面,从而不会出现UI无响应的情况。那如果在这2s内,如:1.2秒时,Mail突然返回了数据,会打断其他的任务吗?不会。因为1.2后接受到Mail的返回时JS会把Mail的回调函数插入事件循环,等待当前任务执行完毕后才会从事件循环中取出Mail的回调函数并执行。

    +

    我们刚刚碰到了两个新的名词:回调函数事件循环。我们来解释一下JavaScript中是怎么用他们来实现异步编程的。

    回调函数

    从现在开始我们只讲异步模型了,请忘掉同步模型。

    -

    如图5所示,"Saved the cheque to my bank account"就是回调函数,因为只有等待朋友把包含cheque的邮件寄回来了,才可以执行,而且回调函数的调用(通知)方应该是邮局,因为它能精准的知道回复邮件什么时候到。

    -

    让我们来看一下代码 CallBackDemo

    +

    什么是回调函数?如图5所示,"Saved the cheque to my bank account"就是回调函数,因为只有等待朋友把包含cheque的邮件寄回来了,才可以执行,而且回调函数的调用(通知)方应该是第三方的邮局,因为它能精准的知道回复邮件什么时候到。

    +

    让我们继续从代码层面来看一下寄信的例子 点击查看源码 MailDemo CallBack

    CallBack Code

    Figure 6. CallBack code in mailing example

    -

    我们可以看到callBackFromPostOffice()就是CallBack函数,Post Office是给我发了个消息,然后我自己调用的这个函数。(因为我的Demo是用多线程编写,所以用消息进行通信是很重要的一方式,尽量避免在新线程里面直接调用主线程的函数。JavaScript是单线程。)

    -

    我的Demo为了方便在Post Office拿到回复邮件后直接调用了唯一的回调函数,但是JavaScript并不仅仅这一个回调函数,那这些回调函数存储在什么地方呢?

    -

    队列。先进先出。然后事件循环会不断的查询Post Office有没有获取到邮件,一旦获取就会把回调函数callBackFromPostOffice()插入到Queue里面,之后事件循环又会看Queue里面有没有等待执行的回调函数,有就执行。(按理来讲这里面应该有两个Queue:IO queue, CallBack Queue)

    -

    回调函数,就是等拿到结果数据后对该数据进行函数的函数。

    +

    我们可以看到callBackFromPostOffice()就是CallBack函数,Post Office收到信之后给我发了个消息,然后我自己调用的这个函数。(因为我的Demo是用多线程编写,所以用消息进行通信是很重要的一方式,尽量避免在新线程里面直接调用主线程的函数。JavaScript是单线程。)

    +

    我的Demo因为是单一事件,所以在Post Office拿到回复邮件后直接调用了唯一的回调函数,但是一般的用户页面上充斥着各种不同的事件,那这些回调函数存储在什么地方呢?怎么处理这些回调函数呢?

    +

    JavaScript使用队列(先进先出)存储这些回调函数。然后事件循环会不断的查询Post Office有没有获取到邮件,一旦获取就会把回调函数callBackFromPostOffice()插入到Queue里面,然后又会查看Queue里面有没有等待执行的回调函数,有就执行。

    +

    总结一下:回调函数就是等拿到结果数据之后对该数据进行处理的函数(把朋友寄回的支票存到银行)。

    事件循环(EventLoop)

    -

    如果让我起名字可能会起TaskLoop,因为它的核心作用就是不断的去执行回调函数,如果我们把一个回调函数当作一个task的话,大部分时间处理的就是task。

    +

    如果让我起名字可能会起TaskLoop,因为它的核心作用就是不断的去执行回调函数,如果我们把一个回调函数当作一个任务(Task)的话,大部分时间处理的就是Task。当然它还会检查朋友的邮件有没有寄回来(IO操作有没有完成,定时器有没有归零)。

    当然JavaScript最早是应用于Browser,大部分的task都是来自用户的操作:点击,鼠标经过,页面加载等待。这些都是事件,为了异步处理这些事件,这个loop所以叫做EventLoop。

    -

    先看一下我们自己的示例 Mailing Demo

    +

    我把之前的邮寄的例子改变成了用事件循环来实现,来看一下 点击查看源码 MailDemo EventLoop

    EventLoop Code

    Figure 7. EventLoop in mailing example

    实事上,我们执行了eventLoop.addTask()之后,callBackFromPostOffice()并不会马上执行,要等待eventLoop.run()执行检查Queue时发现有等待执行的任务(回调函数)才会执行。

    -

    JavaScript EventLoop在Browser和Node.js中的实现是我这个示例的增强版(JavaScript只是一个语言,具体的语法解析和实现需要其他的平台如Chrome或者Node.js)。只是事件循环的任务类型和触发时间会复杂一些,而且Browser和nNode.js也不一样。

    -

    事件循环是什么?需要异步执行时把回调函数插入队列,事件循环里面会检查该队列,如果有待执行任务就去执行。当然还有一些细节,比如文件和网络操作,怎么知道他们有没有拿到结果?我只能拿到结果数据了才能调用对应的回调函数,不然调用就会出错。我们接下来看Node.js是怎么实现的。

    +

    JavaScript EventLoop在Browser和Node.js中的实现是我这个示例的增强版(JavaScript只是一个语言,具体的语法解析和上体的功能实现需要一个Runtime如Chrome或者Node.js,他们的语法解析都是用的V8)。只是事件循环的任务类型和触发时间会比我这个示例复杂很多,而且Browser和Node.js也不一样。

    +

    事件循环是什么?用户需要异步执行某些函数时把回调函数插入队列,事件循环里面会检查该队列,如果有待执行任务就去执行。对于异步的IO操作,比如文件和网络操作,也会定期检查操作有没有成功,成功则会在事件循环中插入回调函数。我们接下来看Node.js是怎么实现的。

    Node.js

    -

    我们先来看一下流程图,Node.js的事件循环和异步操作由UV库来管理。对应的代码请参考Node.js EventLoop

    +

    我们先来看一下流程图,Node.js的事件循环和异步操作由UV库来管理。图8对应的代码请参考Node.js EventLoop

    EventLoop in Node.js

    Figure 8. EventLoop in Node.js

    -

    查看Node.js Loop中的数据结构源码(Linux平台) | 查看Node.js EventLoop源码

    +

    查看图9有关Node.js Loop中的数据结构源码(Linux平台) | 查看图8有关Node.js EventLoop源码

    EventLoop in Node.js

    Figure 9. Main relationship of the EventLoop in Node.js

    -

    *当所有的IO处理完毕,回调函数执行完毕,EventLoop便会退出。

    -

    我来讲一下这个事件循环的每一步:

    +

    *当所有的IO处理完毕,回调函数执行完毕,EventLoop便会退出

    +

    我来讲一下这个事件循环的每一步(timers=>Pending Queue=>Idle Handlers=>Prepare Handlers=>IO Poll=>Check=>Close):

    timers: 执行setTimtout()和setInterval()的设置的回调函数。 这里有两个有意思的事情: EventLoop Timer in Node.js

    @@ -262,29 +260,29 @@

    Node.js

      -
    1. timers对应的数据结构是小项堆。堆这种结构是部分排序,在处理timer时,它每次只需取出最小的timer,也就是最近那个 (点击查看EventLoop Timer源码)
    2. -
    3. 让我们来复习一下堆排序的时间复杂度:要为两个主要步骤建立树取出元素。假设有n个元素,建立树(插入所有元素)的时间复杂度是O(n),取出所有元素包含重建树的阶段的时间复杂度是O(nlogn)。堆排序是部分排序,取出一次调整一次树,不像其他的排序一次排序后,取出的时间复杂度就是O(n)了。
    4. -
    5. Node.js的timer是基于遍历机制,而且很可能被其他的操作阻塞。所以我们可以确认setTimer()设置的定时器肯定是不准确的。
    6. +
    7. timers对应的数据结构是小项堆。堆这种结构是部分排序,在处理timer时,它每次只需取出最小的timer,也就是最近那个 (点击查看图9.1关于EventLoop Timer源码)
    8. +
    9. 让我们来复习一下堆排序的时间复杂度:要为两个主要步骤建立树取出元素。假设有n个元素,建立树(插入所有元素)的时间复杂度是O(n),取出所有元素包含重建树的阶段的时间复杂度是O(nlogn)。堆排序是部分排序,取出一次调整一次树,不像其他的排序算法一次排序后,取出所有的时间复杂度就是O(n),单次O(1)。
    10. +
    11. Node.js的timer是基于遍历机制,而且很可能被其他的回调函数甚至另一个timer的回调函数阻塞。所以我们可以确认的是,setTimer()设置的定时器肯定是不准确的。要设置很精确的计时器那又是一个话题了。

    Pending Queue: 执行IO Poll阶段里面遗留的一些任务,一般来讲IO Poll阶段如果发现IO操作完成会即时处理掉对应的CallBack函数,但是有些情况会需要触发新的回调函数如出错了。就会放在Pending Queue,在下一轮来处理。(其实现在的代码会在IO Poll阶段后执行8轮的Pending Queue的清空,我也很好奇这个数字8是怎么来的。具体可以参考图8中间的代码)

    Idle Handers&Prepare Handlers: 由UV库内部使用。

    -

    IO Poll: 这里就是去看一下这些IO操作有没有完成,完成了就执行对应的回调函数,或者需要进一步处理就会建新的回调函数放在Pending Queue里面。比如看读取某个文件有没有读完,HTTP的API请求有没有返回结果,进行的DNS查询有没有返回结果等等。

    -

    这一步可是非常关键,也是非常影响性能的一个阶段,在Linux平台上用了epoll模型,当年Nginx用epoll实现了极高的性能。Windows有对应的完成端口(IOCP).Mac的叫Kqueue.不过这个模型只针对网络socket访问。这里的细节我会在下一篇文章介绍。

    +

    IO Poll: 这里就是去看一下这些IO操作有没有完成,完成了就执行对应的回调函数,需要进一步处理就会建新的回调函数放在Pending Queue里面。比如看读取某个文件有没有读完,HTTP的API请求有没有返回结果,进行的DNS查询有没有返回结果等等。

    +

    这一步可是非常关键,也是非常影响性能的一个阶段,在Linux平台上用了epoll模型,当年Nginx用epoll实现了极高的性能。Windows有对应的完成端口(IOCP).Mac的叫Kqueue。这里的细节我会在下一篇文章介绍。

    Check: 执行setImmediate()设置的回调函数。

    Close: 执行关闭请求的回调函数,如socket.on('Close', ...)。与网络操作相关,与文件操作无关。

    -
    微任务
    -

    在JavaScript语言中,除了用setTimeout,setInterval和setImmediate设置自定义的异步函数,还可以用process.nextTick, Promise和Await(Promise的高级实现)来把自己的异步回调函数插入事件循环。关于Promise和Await的细节我会在下一篇文章中提及。

    -

    上节中介绍的那些任务我们称之为宏任务,process.nextTick()和Promise.then()插入的异步函数我们称之为微任务。那这些微任务在什么时候执行呢?

    +

    微任务

    +

    在JavaScript语言中,除了用setTimeout(),setInterval()和setImmediate()设置自定义的异步函数,还可以用process.nextTick(), Promise()和Await()[Promise()的高级实现]来把自己想执行的异步回调函数(可以是任何函数)插入事件循环。关于Promise和Await的细节我会在下一篇文章中提及。

    +

    process.nextTick()和Promise.then()插入的异步函数我们就称之为微任务。上节中介绍的那些任务(setTimeout()、setInterval()、IO和setImmediate())我们称之为宏任务。那微任务和宏任务都在什么时候执行呢?

    Tasks Execution Process in Node.js

    Figure 10. Tasks Execution Process in Node.js

    -

    我们可以看到,用户通过Promise.then()插入的回调函数优先级是非常高的,会在同步代码时间执行完后立刻执行。而且在每种任务如: setTimeout(), setInterval(), I/O Poll执行完后都会检查一下有没有微任务,有的话立即执行。

    +

    我们可以看到,微任务的优先级是非常高的,会在同步代码时间执行完后立刻执行。而且在每种任务如: setTimeout(), setInterval(), I/O Poll执行完后都会检查一下有没有微任务,有的话立即执行。

    注意:

    1. process.nextTick()插入的函数优先级高于Promise.then()插入的函数。
    2. -
    3. 在每执行完一个任务会就会检查一下,并不是执行完队列才会检查微任务Related Pull Request
    4. +
    5. 在每执行完一个任务会就会检查一下微任务队列,并不是执行完某种宏任务队列后才会检查微任务Related Pull Request

    我自己写了一个程序来检测一下不同的异步任务的执行顺序点击查看源码

    Tasks Execution Order in Node.js

    @@ -292,6 +290,7 @@
    微任务
    Figure 10. Tasks Execution Order in Node.js

    +

    我们可以和之前的代码流程图做个比较,完全一致。

    Browser

    在浏览器中事件循环有一些不一样:

      @@ -301,6 +300,10 @@

      Browser

    1. 把同步代码执行一次就算第一个宏任务
    2. Event Loop里面执行顺序是一个宏任务=>执行所有微任务=>所有的渲染任务。执行顺序和Node.js类似,但是Node.js里面会先把所有微任务执行一次,而且没有渲染任务。
    +

    总结

    +

    这篇文章我们主要介绍什么是异步编程。通过一个邮局寄信的例子,回调函数,再到Node.js中的事件循环讲角了从生活层面到代码层面的异步是什么样子的。

    +

    下一篇文章我们会讲怎么用JavaScript进行异步编程?和一些相关的底层实现。

    +

    如果您发现我的文章中有错误或者您有什么新的观点可以随时联系我。

    relative resources

    GPU programming from Jim Fan

  • @@ -308,10 +311,41 @@

    relative resources

    +
    +
    +

    Comments

    + +
    + + + comments powered by Disqus + +
    @@ -347,7 +381,7 @@

    Social

  • Android
  • -
  • +
  • async
  • @@ -377,7 +411,7 @@

    Social

  • Hifi
  • -
  • +
  • javascript
  • @@ -401,7 +435,7 @@

    Social

  • monkey
  • -
  • +
  • multithread
  • @@ -416,7 +450,7 @@

    Social

  • Pandas
  • -
  • +
  • programming
  • @@ -504,6 +538,21 @@

    Lin + + + diff --git a/async-and-multithread-programming-1-async-en.html b/async-and-multithread-programming-1-async-en.html index e5af06c4..13f8e428 100755 --- a/async-and-multithread-programming-1-async-en.html +++ b/async-and-multithread-programming-1-async-en.html @@ -126,51 +126,70 @@

    -

    整个系列的目录

    +

    Table of Contents for the whole series

      -
    • 1.What is asynchronous programming? (JavaScript)
    • -
    • 1.1怎么在JavaScript中进行异步编程?
    • -
    • 2.其他语言的异步编程
    • -
    • 3.多线程编程 (c++) - a.什么是多线程?与硬件的关系是什么? - b.使用多线程意味着什么? - c.使用多线程的目标是什么?
    • -
    • 4.其他语言的多线程编程
    • -
    • 5.多线程与异步编程的关系 (主要讲GoRotine,分析源码,看一下rust有没有改进)
    • -
    • 6.GPU并行编程
    • +
    • +
        +
      1. What is asynchronous programming? (JavaScript)
      2. +
      +
    • +
    • 1.1 How to perform asynchronous programming in JavaScript?
    • +
    • +
        +
      1. Asynchronous programming in other languages
      2. +
      +
    • +
    • +
        +
      1. Multithreading programming (C++)
      2. +
      +
    • +
    • a. What is multithreading? What is its relationship with hardware?
    • +
    • b. What does it mean to use multithreading?
    • +
    • c. What is the goal of using multithreading?
    • +
    • +
        +
      1. Multithreading programming in other languages
      2. +
      +
    • +
    • +
        +
      1. The relationship between multithreading and asynchronous programming (mainly discussing GoRoutine, analyzing the source code, and examining if Rust has made improvements)
      2. +
      +
    • +
    • +
        +
      1. GPU parallel programming
      2. +
      +
    -

    introduction

    -

    background

    -

    我们这个系列主要是讲异步和多线程。异步和多线程本质上来讲都是为了充分的利用计算资源。

    -

    当CPU单核主频的发展速度达到瓶颈之后,往多核CPU发展。同时GPU的并行运算给LLM的发展有着极大的促进作用。当然我这一个系列主要是讲CPU,这里我只是想表明并行计算这个概念在现在这个时代非常重要。

    -

    那么异步编程是指什么?它和多线程是什么关系?多线程与硬件的关系是什么?异步编程&多线程编程与并行计算又有什么关系?我们将通过这个系列搞清楚这些问题。

    -

    我将会不定期的更新这个系列的文章

    -

    in this article

    -

    在这篇文章里面我会介绍:

    +

    Introduction

    +

    Background (Asynchronous & Multithreading)

    +

    This series mainly discusses asynchronous and multithreading programming. Both asynchronous and multithreading programming aim to fully utilize computing resources.

    +

    As CPU single-core frequency development reached a bottleneck, the trend shifted towards multi-core CPUs. In recent years, parallel matrix operations on GPUs have greatly promoted the development of machine learning and LLMs. This series will mainly focus on CPUs and will not cover GPUs for now. The main goal is to introduce asynchronous and multithreading programming and their relationship.

    +

    So, what is asynchronous programming? How is it related to multithreading? What is the relationship between multithreading and hardware? And how are asynchronous & multithreading programming related to parallel computing? We will address these questions throughout this series.

    +

    (If I discover errors or new viewpoints, I will update the published articles or change the structure of this series)

    +

    In this article

    +

    In this article, I will introduce:

      -
    1. 什么是异步编程?为什么需要它?
    2. -
    3. JavaScript中的异步编程 - a. 回调函数 - b. 事件循环(源码) - b. promise - c. await
    4. -
    5. 怎么判断IO操作有没有结束?(源码) - a. 网络操作 - b. 文件操作 - c. 有没有浪费资源呢? - 对于cpu来讲是没有的,因为就算
    6. +
    7. What is asynchronous programming? Why is it needed?
    8. +
    9. Asynchronous programming in JavaScript
    10. +
    11. a. Callback functions
    12. +
    13. b. Event loop (source code analysis)
    14. +
    15. The next article
    -

    实验环境

    +

    Experimental Environment

    OS: Windows 11 21H2 22000.2538

    Node.js: 20.13.1

    -

    什么是异步编程?

    -

    异步的反面是同步编程。

    +

    What is asynchronous programming?

    +

    What is asynchronous?

    +

    The opposite of asynchronous is synchronous. Let's look at an example from daily life: I go to the post office to mail a letter to borrow money from a friend, and then I deposit the check into the bank after receiving it. I can achieve this using synchronous and asynchronous approaches:

    Basic Introduction to Async and Sync

    Figure 1. Basic Introduction to Async and Sync

    -

    我用Node.js写了一个示例程序,来看看执行结果MailDemo

    +

    I wrote a sample program in Node.js to see the execution results Click to view source code MailDemo

    Synchronized Mailing Demo

    Figure 2. Synchronized Mailing Demo @@ -178,14 +197,14 @@

    什么是异步编程?

    Asynchronized Mailing Demo

    - Figure 3. Synchronized Mailing Demo + Figure 3. Asynchronized Mailing Demo

    -

    所以我们能看到同步和异步最大的差别就是我(寄件人)在把邮件给邮局后是否在邮局等待朋友的回信。

    -

    从这个实例可以看出,很明显的在异步的邮寄过程当中,这个安排更合理。在同步的邮寄程序中有个看似非常奇怪的问题:我为什么把邮件给邮局后,我为什么还要在邮局傻傻的等? 我明明可以去吃饭,逛街,看电影做很多事情。朋友回复邮件了,邮局可以打电话通知我,我再去取啊。

    -

    所以在邮局等待是一个不合理的操作,有一个词叫阻塞经常和同步编程一起出现。阻塞意味着主人翁没有做任何事情的在等待一个结果。发生在现实生活中不合理,同样在程序中也不合理,所以异步编程会解决这个问题。

    -

    在工业界有个领域叫统筹规划,简单来讲就是就是让资源(包括机器、人力等)保持高效运转,从而在相同的时间内完成更多的任务。(在这里我希望大家可以不要像机器一样一直运转,在享受生活的同时,也能赚到很多钱。是的,统筹规划不应该应该到人身上。)

    -

    回到编程我们看一看异步同步的时序图。

    +

    So, the biggest difference we can see between synchronous and asynchronous is whether I (the sender) wait at the post office after handing the letter to the post office or go do other things.

    +

    From this example, it's obvious that the asynchronous mailing process is more reasonable. In the synchronous mailing program, there's a seemingly strange question: Why do I need to wait at the post office after giving the letter? I could be eating, shopping, watching movies, or doing many other things. When my friend replies, the post office can call me, and I can then pick up the letter.

    +

    So, waiting at the post office is an unreasonable operation. A term called blocking often appears with synchronous programming, meaning the protagonist is doing nothing but waiting for a result. This is unreasonable in real life and also in programming, so asynchronous programming solves this problem.

    +

    In the industry, there is a field called operations research, which aims to keep resources (including machines, labor, etc.) running efficiently to complete more tasks in the same amount of time. (I hope everyone can enjoy life while earning money, and not run like machines. Operations research shouldn't apply to humans.)

    +

    Back to programming, let's look at the timing diagrams of asynchronous and synchronous.

    Synchronized Mailing Sequence Diagram

    Figure 4. Synchronized Mailing Sequence Diagram @@ -196,111 +215,119 @@

    什么是异步编程?

    Figure 5. Asynchronized Mailing Sequence Diagram

    -

    寄信这个活动有什么特点呢? - 1. 是邮局来寄,不需要我 - 2. 等待回信需要比较长的时间

    -

    在编程里面我们可以想到类似的操作:文件读写,网络请求(TCP,HTTP,DNS) 等等。

    +

    These two timing diagrams make it clearer that asynchronous is more reasonable. During the mailing process, I can do many things like eating and shopping without waiting at the post office.

    +

    What are the characteristics of this mailing activity? +1. It's handled by someone else (the post office), not myself. +2. Waiting for a reply takes a relatively long time.

    +

    Asynchronous Programming

    +

    In programming, there are some operations similar to mailing a letter, such as file reading/writing, network requests (TCP, HTTP, DNS), etc.

      -
    • 文件读写:磁盘控制器去帮我们找到我们想要的内容
    • -
    • 网络请求:服务器在处理请求
    • +
    • File reading/writing: The disk controller helps us find the content we want.
    • +
    • Network requests: The server processes the request.
    • +
    • Timer: The timer just waits and doesn't need to wait for any return.
    -

    Timer也是,Timer就是不需要等待任何返回,就是单纯的等待。

    -

    异步编程就是让主线程在等待时去做其他的事情。

    -

    JavaScript中的异步编程

    -

    我为什么要讲JavaScript中的异步编程?

    -

    因为JavaScript是设计成单线程的,在Browser里面是事件响应型的模型,而且网络请求(年纪大点的人应该听过Ajax)又多,所以一旦有点阻塞,Browser来不及处理用户的Click和界面的渲染,整个UI就会处于无响应的状态,用户体验就会极差。

    -

    为什么不设计成多线程的?处理多线程极其的麻烦,会让编程者的头发掉光。相比之下异步模型会编写起来会简单很多。

    -

    比如:一个Web page在Browser里面请求一个REST API(HTTP): Mail, 需要等待2s才能回复数据。 -如果是同步:在这2s内,用户点击页面是没有响应的,因为主线程在这2s内的任务是等待Mail的返回,无法做其他的事情。 -如果是异步:这2s内主线程是空闲的,完全可以处理用户的任何请求,而不会出现UI无响应的情况。那如果在这2s内,如:1.2秒,Mail突然返回了数据,会打断其他的任务吗?不会。因为1.2后接受到Mail的返回时JS会把Mail的回调函数插入事件循环,等待当前任务执行完毕后才会从事件循环中取出Mail的回调函数并执行相应的处理任务。

    -

    我们刚刚碰到了两个新的名词:事件循环回调函数。我们来解释一下JavaScript中是怎么用他们来实现异步编程的。

    -

    回调函数

    -

    从现在开始我们只讲异步模型了,请忘掉同步模型。

    -

    如图5所示,"Saved the cheque to my bank account"就是回调函数,因为只有等待朋友把包含cheque的邮件寄回来了,才可以执行,而且回调函数的调用(通知)方应该是邮局,因为它能精准的知道回复邮件什么时候到。

    -

    让我们来看一下代码 CallBackDemo

    +

    The disk controller and server are the third parties, equivalent to the post office in the previous example. The time it takes to handle these tasks depends on them, and I can't control it.

    +

    To summarize: Asynchronous programming allows the main thread to perform other tasks while waiting for some tasks to be handled by a third party.

    +

    Asynchronous Programming in JavaScript

    +

    Why do I want to discuss asynchronous programming in JavaScript?

    +

    Because JavaScript was initially designed to be single-threaded, it follows an event-driven model in the browser, and network requests (such as Ajax) are frequent. If there is any blocking, the browser can't process user clicks and render the interface in time, leading to an unresponsive UI and poor user experience. (The JavaScript browser environment has some auxiliary threads in addition to the main thread)

    +

    Why not design it to be multithreaded? Handling multithreading is extremely complicated and causes more headaches for developers. Comparatively, the asynchronous model is much simpler to write than multithreading.

    +

    For example, a web page in the browser requests a REST API (HTTP): Mail, which takes 2 seconds to return data. +- If it's synchronous: During these 2 seconds, user clicks on the page are unresponsive because the main thread can only wait for the Mail to return and cannot do other things. +- If it's asynchronous: During these 2 seconds, the main thread is idle and can handle any user requests and render the page, avoiding unresponsive UI. If the Mail suddenly returns data at 1.2 seconds, it will not interrupt other tasks. When the Mail returns at 1.2 seconds, JavaScript will insert the Mail's callback function into the event loop, waiting for the current task to complete before retrieving and executing the Mail's callback function from the event loop.

    +

    We encountered two new terms: callback function and event loop. Let's explain how JavaScript uses them to achieve asynchronous programming.

    +

    Callback function

    +

    From now on, we will only talk about the asynchronous model. Please forget the synchronous model.

    +

    What is a callback function? As shown in Figure 5, "Saved the cheque to my bank account" is a callback function because it can only be executed after my friend sends back the cheque, and the caller (notifier) should be the third-party post office because it knows precisely when the reply letter arrives.

    +

    Let's continue to look at the mailing example from the code perspective Click to view source code MailDemo CallBack

    CallBack Code

    Figure 6. CallBack code in mailing example

    -

    我们可以看到callBackFromPostOffice()就是CallBack函数,Post Office是给我发了个消息,然后我自己调用的这个函数。(因为我的Demo是用多线程编写,所以用消息进行通信是很重要的一方式,尽量避免在新线程里面直接调用主线程的函数。JavaScript是单线程。)

    -

    我的Demo为了方便在Post Office拿到回复邮件后直接调用了唯一的回调函数,但是JavaScript并不仅仅这一个回调函数,那这些回调函数存储在什么地方呢?

    -

    队列。先进先出。然后事件循环会不断的查询Post Office有没有获取到邮件,一旦获取就会把回调函数callBackFromPostOffice()插入到Queue里面,之后事件循环又会看Queue里面有没有等待执行的回调函数,有就执行。(按理来讲这里面应该有两个Queue:IO queue, CallBack Queue)

    -

    回调函数,就是等拿到结果数据后对该数据进行函数的函数。

    -

    事件循环(EventLoop)

    -

    如果让我起名字可能会起TaskLoop,因为它的核心作用就是不断的去执行回调函数,如果我们把一个回调函数当作一个task的话,大部分时间处理的就是task。

    -

    当然JavaScript最早是应用于Browser,大部分的task都是来自用户的操作:点击,鼠标经过,页面加载等待。这些都是事件,为了异步处理这些事件,这个loop所以叫做EventLoop。

    -

    先看一下我们自己的示例 Mailing Demo

    +

    We can see that callBackFromPostOffice() is the CallBack function. The post office sends me a message after receiving the letter, and then I call this function myself. (Because my demo is written in multithreading, using message communication is very important to avoid directly calling main thread functions from new threads. JavaScript is single-threaded.)

    +

    My demo uses a single event, so after the post office receives the reply letter, it directly calls the only callback function. However, a typical user interface is filled with various events. Where are these callback functions stored? How are they handled?

    +

    JavaScript uses a queue (first in, first out) to store these callback functions. The event loop constantly checks if the post office has received a letter, and if it has, it inserts the callback function callBackFromPostOffice() into the queue, then checks if there are waiting callback functions in the queue, and executes them.

    +

    To summarize: A callback function processes the result data after receiving it (depositing the friend's cheque into the bank).

    +

    Event loop

    +

    If I were to name it, I might call it TaskLoop because its core function is to execute callback functions continuously. If we consider each callback function as a task, most of the time is spent handling tasks. Of course, it also checks if the friend's mail has arrived (if the IO operation is complete or if the timer has counted down).

    +

    JavaScript was originally used in browsers, and most tasks come from user actions: clicks, mouseovers, page loads, etc. These are all events, so the loop that handles them is called the EventLoop.

    +

    I changed the previous mailing example to use an event loop. Let's look at it Click to view source code MailDemo EventLoop

    EventLoop Code

    Figure 7. EventLoop in mailing example

    -

    实事上,我们执行了eventLoop.addTask()之后,callBackFromPostOffice()并不会马上执行,要等待eventLoop.run()执行检查Queue时发现有等待执行的任务(回调函数)才会执行。

    -

    JavaScript EventLoop在Browser和Node.js中的实现是我这个示例的增强版(JavaScript只是一个语言,具体的语法解析和实现需要其他的平台如Chrome或者Node.js)。只是事件循环的任务类型和触发时间会复杂一些,而且Browser和nNode.js也不一样。

    -

    事件循环是什么?需要异步执行时把回调函数插入队列,事件循环里面会检查该队列,如果有待执行任务就去执行。当然还有一些细节,比如文件和网络操作,怎么知道他们有没有拿到结果?我只能拿到结果数据了才能调用对应的回调函数,不然调用就会出错。我们接下来看Node.js是怎么实现的。

    +

    In fact, after we execute eventLoop.addTask(), callBackFromPostOffice() will not execute immediately. It must wait for eventLoop.run() to check the queue and find that there are tasks (callback functions) waiting to be executed before it runs.

    +

    The JavaScript EventLoop in the browser and Node.js is an enhanced version of this example (JavaScript is just a language. Specific syntax parsing and underlying functionality need a runtime like Chrome or Node.js, which both use the V8 engine for syntax parsing). The task types and trigger times of the event loop are more complex than in this example, and there are differences between the browser and Node.js.

    +

    What is the event loop? When the user needs to execute some functions asynchronously, the callback function is inserted into the queue, and the event loop checks the queue and executes the tasks if there are any. For asynchronous IO operations, such as file and network operations, it also regularly checks if the operation is successful, and if so, inserts the callback function into the event loop. Next, let's see how Node.js implements this.

    Node.js

    -

    我们先来看一下流程图,Node.js的事件循环和异步操作由UV库来管理。对应的代码请参考Node.js EventLoop

    +

    Let's look at the flowchart first. The event loop and asynchronous operations in Node.js are managed by the UV library. The code corresponding to Figure 8 can be found here Node.js EventLoop

    EventLoop in Node.js

    Figure 8. EventLoop in Node.js

    -

    查看Node.js Loop中的数据结构源码(Linux平台) | 查看Node.js EventLoop源码

    +

    See Figure 9 for the source code related to the data structure of the Node.js Loop (Linux platform) | See Figure 8 for the source code related to the Node.js EventLoop

    EventLoop in Node.js

    Figure 9. Main relationship of the EventLoop in Node.js

    -

    *当所有的IO处理完毕,回调函数执行完毕,EventLoop便会退出。

    -

    我来讲一下这个事件循环的每一步:

    -

    timers: 执行setTimtout()和setInterval()的设置的回调函数。 -这里有两个有意思的事情: +

    When all IO operations are complete and callback functions are executed, the EventLoop will exit.

    +

    Let's talk about each step of this event loop (timers=>Pending Queue=>Idle Handlers=>Prepare Handlers=>IO Poll=>Check=>Close):

    +

    timers: Execute the callback functions set by setTimeout() and setInterval(). +There are two interesting things here: EventLoop Timer in Node.js

    Figure 9.1 EventLoop Timer in Node.js

      -
    1. timers对应的数据结构是小项堆。堆这种结构是部分排序,在处理timer时,它每次只需取出最小的timer,也就是最近那个 (点击查看EventLoop Timer源码)
    2. -
    3. 让我们来复习一下堆排序的时间复杂度:要为两个主要步骤建立树取出元素。假设有n个元素,建立树(插入所有元素)的时间复杂度是O(n),取出所有元素包含重建树的阶段的时间复杂度是O(nlogn)。堆排序是部分排序,取出一次调整一次树,不像其他的排序一次排序后,取出的时间复杂度就是O(n)了。
    4. -
    5. Node.js的timer是基于遍历机制,而且很可能被其他的操作阻塞。所以我们可以确认setTimer()设置的定时器肯定是不准确的。
    6. +
    7. The data structure corresponding to timers is a min-heap. A heap is partially sorted; in handling timers, it only needs to take out the smallest timer each time, which is the closest one (Click to see the source code related to the EventLoop Timer).
    8. +
    9. Let's review the time complexity of heap sort: For two main steps, building the tree and removing elements. Assuming there are n elements, the time complexity of building the tree (inserting all elements) is O(n), and the time complexity of removing all elements, including rebuilding the tree, is O(nlogn). Heap sort is partial sorting, taking out and adjusting the tree once each time, unlike other sorting algorithms that sort once and take out all with a time complexity of O(n) and single O(1).
    10. +
    11. Node.js timers are based on a polling mechanism and may be blocked by other callback functions or even another timer's callback function. So, we can confirm that timers set by setTimer() are inaccurate. Setting very precise timers is another topic.
    -

    Pending Queue: 执行IO Poll阶段里面遗留的一些任务,一般来讲IO Poll阶段如果发现IO操作完成会即时处理掉对应的CallBack函数,但是有些情况会需要触发新的回调函数如出错了。就会放在Pending Queue,在下一轮来处理。(其实现在的代码会在IO Poll阶段后执行8轮的Pending Queue的清空,我也很好奇这个数字8是怎么来的。具体可以参考图8中间的代码)

    -

    Idle Handers&Prepare Handlers: 由UV库内部使用。

    -

    IO Poll: 这里就是去看一下这些IO操作有没有完成,完成了就执行对应的回调函数,或者需要进一步处理就会建新的回调函数放在Pending Queue里面。比如看读取某个文件有没有读完,HTTP的API请求有没有返回结果,进行的DNS查询有没有返回结果等等。

    -

    这一步可是非常关键,也是非常影响性能的一个阶段,在Linux平台上用了epoll模型,当年Nginx用epoll实现了极高的性能。Windows有对应的完成端口(IOCP).Mac的叫Kqueue.不过这个模型只针对网络socket访问。这里的细节我会在下一篇文章介绍。

    -

    Check: 执行setImmediate()设置的回调函数。

    -

    Close: 执行关闭请求的回调函数,如socket.on('Close', ...)。与网络操作相关,与文件操作无关。

    -
    微任务
    -

    在JavaScript语言中,除了用setTimeout,setInterval和setImmediate设置自定义的异步函数,还可以用process.nextTick, Promise和Await(Promise的高级实现)来把自己的异步回调函数插入事件循环。关于Promise和Await的细节我会在下一篇文章中提及。

    -

    上节中介绍的那些任务我们称之为宏任务,process.nextTick()和Promise.then()插入的异步函数我们称之为微任务。那这些微任务在什么时候执行呢?

    +

    Pending Queue: Execute some tasks left over from the IO Poll phase. Generally, when the IO Poll phase finds that the IO operation is complete, it will handle the corresponding callback function immediately, but in some cases, it may trigger new callback functions, such as errors, which will be placed in the Pending Queue to be processed in the next round. (In fact, the current code will execute the Pending Queue 8 times after the IO Poll phase. I'm also curious about the number 8. Refer to the middle code in Figure 8)

    +

    Idle Handers&Prepare Handlers: Used internally by the UV library.

    +

    IO Poll: Here, it checks if these IO operations are complete, and if so, it executes the corresponding callback function. If further processing is needed, it creates new callback functions and places them in the Pending Queue. For example, checking if a file read is complete, if the HTTP API request has returned results, if the DNS query has returned results, etc.

    +

    This step is crucial and significantly impacts performance. The epoll model is used on the Linux platform, which achieved extremely high performance when implemented by Nginx. Windows has corresponding completion ports (IOCP). Mac's is called Kqueue. I will introduce these details in the next article.

    +

    Check: Execute the callback functions set by setImmediate().

    +

    Close: Execute the callback functions for close requests, such as socket.on('Close', ...). Related to network operations, not file operations.

    +

    Microtasks

    +

    In the JavaScript language, besides using setTimeout(), setInterval(), and setImmediate() to set custom asynchronous functions, you can also use process.nextTick(), Promise(), and Await() [the advanced implementation of Promise()] to insert the callback functions (any function) you want to execute asynchronously into the event loop. I will mention the details of Promise and Await in the next article.

    +

    Functions inserted by process.nextTick() and Promise.then() are called microtasks. The other tasks mentioned in the previous section (setTimeout(), setInterval(), IO, and setImmediate()) are called macro tasks. When are microtasks and macro tasks executed?

    Tasks Execution Process in Node.js

    Figure 10. Tasks Execution Process in Node.js

    -

    我们可以看到,用户通过Promise.then()插入的回调函数优先级是非常高的,会在同步代码时间执行完后立刻执行。而且在每种任务如: setTimeout(), setInterval(), I/O Poll执行完后都会检查一下有没有微任务,有的话立即执行。

    -

    注意:

    +

    We can see that the priority of microtasks is very high and will be executed immediately after the synchronous code is executed. Moreover, after each type of task, such as setTimeout(), setInterval(), I/O Poll, is executed, the microtask queue is checked, and if there are any, they are executed immediately.

    +

    Note:

      -
    1. process.nextTick()插入的函数优先级高于Promise.then()插入的函数。
    2. -
    3. 在每执行完一个任务会就会检查一下,并不是执行完队列才会检查微任务Related Pull Request
    4. +
    5. The priority of functions inserted by process.nextTick() is higher than those inserted by Promise.then().
    6. +
    7. The microtask queue is checked after each task is executed, not after the entire macro task queue is executed Related Pull Request.
    -

    我自己写了一个程序来检测一下不同的异步任务的执行顺序点击查看源码

    +

    I wrote a program to check the execution order of different asynchronous tasks Click to view source code

    Tasks Execution Order in Node.js

    Figure 10. Tasks Execution Order in Node.js

    +

    We can compare it with the previous code flowchart, which matches completely.

    Browser

    -

    在浏览器中事件循环有一些不一样:

    +

    The event loop in the browser is a bit different:

      -
    1. 没有process.nextEvent()
    2. -
    3. 有MutationObserver()任务,主要用来监控网络上元素属性的变化,也属于微任务
    4. -
    5. 有渲染任务(其实Browser是有专门的渲染线程的,是接收主线程渲染任务发送的指令,然后渲染线程负责具体的实施,为了高效渲染不阻塞主线程)
    6. -
    7. 把同步代码执行一次就算第一个宏任务
    8. -
    9. Event Loop里面执行顺序是一个宏任务=>执行所有微任务=>所有的渲染任务。执行顺序和Node.js类似,但是Node.js里面会先把所有微任务执行一次,而且没有渲染任务。
    10. +
    11. No process.nextEvent()
    12. +
    13. There is a MutationObserver() task, mainly used to monitor changes in network element attributes, which also belongs to microtasks
    14. +
    15. There are rendering tasks (actually, the browser has a dedicated rendering thread that receives rendering tasks sent by the main thread, and the rendering thread is responsible for the specific implementation to render efficiently without blocking the main thread)
    16. +
    17. Synchronous code execution is considered the first macro task
    18. +
    19. In the Event Loop, the execution order is one macro task => execute all microtasks => all rendering tasks. The execution order is similar to Node.js, but in Node.js, all microtasks are executed first, and there are no rendering tasks.
    +

    Summary

    +

    In this article, we mainly introduced what asynchronous programming is. Through a mailing example, callback functions, and then to the event loop in Node.js, we explained what asynchronous looks like from a life perspective to the code level.

    +

    In the next article, we will discuss how to perform asynchronous programming in JavaScript and some related underlying implementations.

    +

    If you find any errors in my article or have any new viewpoints, feel free to contact me or leave a comment.

    relative resources

    GPU programming from Jim Fan

    @@ -308,10 +335,41 @@

    relative resources

    +
    +
    +

    Comments

    + +
    + + + comments powered by Disqus + +
    @@ -347,7 +405,7 @@

    Social

  • Android
  • -
  • +
  • async
  • @@ -377,7 +435,7 @@

    Social

  • Hifi
  • -
  • +
  • javascript
  • @@ -401,7 +459,7 @@

    Social

  • monkey
  • -
  • +
  • multithread
  • @@ -416,7 +474,7 @@

    Social

  • Pandas
  • -
  • +
  • programming
  • @@ -504,6 +562,21 @@

    Lin + + + diff --git a/async-and-multithread-programming-1-async-in-javascript-cn.html b/async-and-multithread-programming-1-async-in-javascript-cn.html index 20df8340..f965ee98 100755 --- a/async-and-multithread-programming-1-async-in-javascript-cn.html +++ b/async-and-multithread-programming-1-async-in-javascript-cn.html @@ -1,7 +1,7 @@ - Async and multithread programming - 1 Async in JavaScript - TechLoveDeath + 异步和多线程自编程 - 1 JavaScript中的异步 - TechLoveDeath @@ -18,7 +18,7 @@ - + @@ -97,8 +97,8 @@

    - Async and multithread programming - 1 Async in JavaScript + title="Permalink to 异步和多线程自编程 - 1 JavaScript中的异步"> + 异步和多线程自编程 - 1 JavaScript中的异步

    @@ -150,11 +150,13 @@

    in this article

    1. JavaScript中的异步编程 b. promise c. await -2. 怎么判断IO操作有没有结束?(源码) +2. Dive dep into it:怎么判断IO操作有没有结束?(源码) a. 网络操作 b. 文件操作 c. 有没有浪费资源呢? - 对于cpu来讲是没有的,因为就算

    + 对于cpu来讲是没有的,因为就算 +3. 头脑风暴 + a. 怎么让epoll实现自定义的事件通知

    实验环境

    OS: Windows 11 21H2 22000.2538

    Node.js: 20.13.1

    @@ -180,10 +182,41 @@

    relative resources

    +
    +
    +

    Comments

    + +
    + + + comments powered by Disqus + +
    @@ -219,7 +252,7 @@

    Social

  • Android
  • -
  • +
  • async
  • @@ -249,7 +282,7 @@

    Social

  • Hifi
  • -
  • +
  • javascript
  • @@ -273,7 +306,7 @@

    Social

  • monkey
  • -
  • +
  • multithread
  • @@ -288,7 +321,7 @@

    Social

  • Pandas
  • -
  • +
  • programming
  • @@ -376,6 +409,21 @@

    Lin + + + diff --git a/async-and-multithread-programming-1-async-in-javascript-en.html b/async-and-multithread-programming-1-async-in-javascript-en.html new file mode 100755 index 00000000..0e8057ab --- /dev/null +++ b/async-and-multithread-programming-1-async-in-javascript-en.html @@ -0,0 +1,428 @@ + + + + Async&Multithrad - 1 Async - 1 Async in JavaScript - TechLoveDeath + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    +

    content

    +
      +
    1. What is asynchronous programming? (JavaScript) + 1.1 怎么在JavaScript中进行异步编程?
    2. +
    3. 其他语言的异步编程
    4. +
    5. 多线程编程 (c++) + a. 什么是多线程?与硬件的关系是什么? + b. 使用多线程意味着什么? + c. 使用多线程的目标是什么?
    6. +
    7. 其他语言的多线程编程
    8. +
    9. 多线程与异步编程的关系 (主要讲GoRotine,分析源码,看一下rust有没有改进)
    10. +
    11. GPU并行编程
    12. +
    +

    introduction

    +

    background

    +

    我们这个系列主要是讲异步和多线程。异步和多线程本质上来讲都是为了充分的利用计算资源。

    +

    当CPU单核主频的发展速度达到瓶颈之后,往多核CPU发展。同时GPU的并行运算给LLM的发展有着极大的促进作用。当然我这一个系列主要是讲CPU,这里我只是想表明并行计算这个概念在现在这个时代非常重要。

    +

    那么异步编程是指什么?它和多线程是什么关系?多线程与硬件的关系是什么?异步编程&多线程编程与并行计算又有什么关系?我们将通过这个系列搞清楚这些问题。

    +

    我将会不定期的更新这个系列的文章

    +

    in this article

    +

    在这篇文章里面我会介绍: +1. JavaScript中的异步编程 + b. promise + c. await +2. 怎么判断IO操作有没有结束?(源码) + a. 网络操作 + b. 文件操作 + c. 有没有浪费资源呢? + 对于cpu来讲是没有的,因为就算

    +

    实验环境

    +

    OS: Windows 11 21H2 22000.2538

    +

    Node.js: 20.13.1

    +

    Promise

    +

    为什么要讲promise?

    +

    代码github链接

    +

    画流程图,标注一下与回调函数的关系 callbacks

    +

    解释

    +

    await

    +

    “async” 这个单词表达了一个简单的事情:即这个函数总是返回一个 promise,如果正常return,就是resolve(returned value),如果异常或者主动返回一个reject了的promise也可以触发reject

    +

    await 接受thenable(什么是thenable,只要这个对象具有符合 Promise/A+ 规范的 then 方法即可),如果不是thenable,是具体的值,会立刻解决

    +

    await只能和async配合使用,async可以单独使用。

    +

    怎么判断IO操作有没有结束?(源码)

    +

    网络操作

    +

    文件操作

    +

    异步真的没有浪费资源吗?

    +

    事实上在同步编程里面,如果某线程傻傻等待不做任何事件,CPU也不会浪费资源,因为操作系统会把CPU的时间片分配给其他的进程。但这就是操作系统层面的调度了,但是对于线程层面的调度来讲,还是需要异步操作。那么对于进程层面的调度呢?是可以用多线程来解决的,但是比较麻烦。其实异步操作里面大都用了多线程,异步操作和多线程的关系还是很紧密的。简单来讲,现在的异步操作同时大大简化了多线程的管理。

    +

    在讲多线程之前,我们会浏览一下异步操作在其他编程语言上的实现。更有利于我们去了解异步与多线程之间的关系。

    +

    relative resources

    +

    GPU programming from Jim Fan

    +
    + + +
    +
    +

    Comments

    + +
    + + + comments powered by Disqus + +
    +
    +
    + +
    + +
    +
    + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/author/bryce.html b/author/bryce.html index da9c40ae..39cf0a0c 100755 --- a/author/bryce.html +++ b/author/bryce.html @@ -83,12 +83,19 @@

    + +

    -
    -
    @@ -204,7 +206,7 @@

    Social

  • Android
  • -
  • +
  • async
  • @@ -234,7 +236,7 @@

    Social

  • Hifi
  • -
  • +
  • javascript
  • @@ -258,7 +260,7 @@

    Social

  • monkey
  • -
  • +
  • multithread
  • @@ -273,7 +275,7 @@

    Social

  • Pandas
  • -
  • +
  • programming
  • @@ -361,6 +363,21 @@

    Lin + + + diff --git a/author/bryce2.html b/author/bryce2.html index ee56c7b0..488dfc75 100755 --- a/author/bryce2.html +++ b/author/bryce2.html @@ -82,6 +82,13 @@
    + +

    - -
    @@ -205,7 +207,7 @@

    Social

  • Android
  • -
  • +
  • async
  • @@ -235,7 +237,7 @@

    Social

  • Hifi
  • -
  • +
  • javascript
  • @@ -259,7 +261,7 @@

    Social

  • monkey
  • -
  • +
  • multithread
  • @@ -274,7 +276,7 @@

    Social

  • Pandas
  • -
  • +
  • programming
  • @@ -362,6 +364,21 @@

    Lin + + + diff --git a/author/bryce3.html b/author/bryce3.html index 86bdd8ab..08ccb34f 100755 --- a/author/bryce3.html +++ b/author/bryce3.html @@ -82,6 +82,13 @@
    + +
    -
    @@ -205,7 +207,7 @@

    Social

  • Android
  • -
  • +
  • async
  • @@ -235,7 +237,7 @@

    Social

  • Hifi
  • -
  • +
  • javascript
  • @@ -259,7 +261,7 @@

    Social

  • monkey
  • -
  • +
  • multithread
  • @@ -274,7 +276,7 @@

    Social

  • Pandas
  • -
  • +
  • programming
  • @@ -362,6 +364,21 @@

    Lin + + + diff --git a/author/bryce4.html b/author/bryce4.html index 99aa0ab8..6bd44a5f 100755 --- a/author/bryce4.html +++ b/author/bryce4.html @@ -82,6 +82,13 @@
    + +

    -
    -
    @@ -205,7 +207,7 @@

    Social

  • Android
  • -
  • +
  • async
  • @@ -235,7 +237,7 @@

    Social

  • Hifi
  • -
  • +
  • javascript
  • @@ -259,7 +261,7 @@

    Social

  • monkey
  • -
  • +
  • multithread
  • @@ -274,7 +276,7 @@

    Social

  • Pandas
  • -
  • +
  • programming
  • @@ -362,6 +364,21 @@

    Lin + + + diff --git a/author/bryce5.html b/author/bryce5.html index ce320201..78c2529e 100755 --- a/author/bryce5.html +++ b/author/bryce5.html @@ -82,6 +82,13 @@
    + +

    -
    -
    @@ -205,7 +207,7 @@

    Social

  • Android
  • -
  • +
  • async
  • @@ -235,7 +237,7 @@

    Social

  • Hifi
  • -
  • +
  • javascript
  • @@ -259,7 +261,7 @@

    Social

  • monkey
  • -
  • +
  • multithread
  • @@ -274,7 +276,7 @@

    Social

  • Pandas
  • -
  • +
  • programming
  • @@ -362,6 +364,21 @@

    Lin + + + diff --git a/author/bryce6.html b/author/bryce6.html index e9da5a5a..44d60ff1 100755 --- a/author/bryce6.html +++ b/author/bryce6.html @@ -82,6 +82,13 @@
    + +

    -
    -
    @@ -205,7 +207,7 @@

    Social

  • Android
  • -
  • +
  • async
  • @@ -235,7 +237,7 @@

    Social

  • Hifi
  • -
  • +
  • javascript
  • @@ -259,7 +261,7 @@

    Social

  • monkey
  • -
  • +
  • multithread
  • @@ -274,7 +276,7 @@

    Social

  • Pandas
  • -
  • +
  • programming
  • @@ -362,6 +364,21 @@

    Lin + + + diff --git a/author/bryce7.html b/author/bryce7.html index 76ebfb80..a90bc3ad 100755 --- a/author/bryce7.html +++ b/author/bryce7.html @@ -82,6 +82,13 @@
    + +

    -
    -