就是没有太理解这一点,虽然知道 IO 操作通过 async/await 异步执行的话不会阻塞主线程运行,但是如果有多个请求的话 node 是如何处理的呢,就是请求本身是否会被异步处理吗,还是说同一时间,只会有一个请求被处理,直到遇到 async/await ,如果有其他请求的话,主线程才会处理这些请求。
由于对并发问题不是特别了解,请各位大佬指教一下。
1
Maboroshii 2022-11-16 11:08:01 +08:00 via Android 1
单线程程序同一时刻只能执行一行代码,await 可以让出 cpu 去执行其他的代码,等 await 有结果了,并且抢到 cpu 了,就回来继续执行。这是我的理解。
|
2
coldmonkeybit OP @Maboroshii 大概明白了,同一时刻只能执行一行代码,就意味着每次只能执行一个请求,当遇到 await 让出 cpu 之后,才能执行第二个请求,如果想要同时处理多个请求,应该就需要用子进程之类的处理方式吧。
|
3
star7th 2022-11-16 11:26:45 +08:00
我没有认真去钻研底层,大多情况下只是一个使用者。但根据我对 nodejs 使用经验,大概是:
在同一个进程里,请求确实是异步处理的。await 挂起一个后,cpu 会处理另一个。但由于很快,所以你可以理解为“接近是同时处理的” 。对不同的两个用户来讲,同时访问 node 接口并没有什么明显延迟感觉 。 如果你是追求非常高的”同时“,那可以多进程处理。比如 eggjs 就可以起多个 work 进程。 好像新出一种特性,可以更细粒度在单进程里模拟多进程,减少进程切换成本。但是没太细了解。总之,如果你要追求高度同步处理,就起多个进程就行。 |
4
tux 2022-11-16 11:27:05 +08:00
不用子进程,正是异步的强大的地方,子进程多了,开销就大,异步没这方面开销,一个一个处理,效率高
|
5
wangtian2020 2022-11-16 11:27:29 +08:00
不要阻塞,指的是不要使用
名字带 Sync 的方法,比如 fs.writeFileSync fs.readFileSync 因为他们会直接卡死主线程,直到结果返回,假如读文件花 1 秒钟,那这一秒钟内任何其他请求都会等待 不阻塞用老的回调方法,或者新的 promise 方法都行 比如 fs.promises.writeFile 举个例子,你写一个请求,读取本地 txt 文件,假如要花一秒钟。如果用非同步 Sync 的方法,那就是 nodejs 告诉系统,你去读这个文件,读完了通知我,我来执行回调。同步就是一直死等,其他事啥也不做。 https://www.zhihu.com/question/62254462/answer/1611867539 同时处理多个请求,不要用阻塞方法就行了,跟子进程没关系。除非你说的请求是,一个循环 1000 万遍的方法,那样真会卡 1 秒钟。 |
6
ysc3839 2022-11-16 11:42:00 +08:00
你需要知道整个程序大致的逻辑是这样的:
while (true) { const 事件 = 等待事件(); if (事件) 事件.处理函数(); } 当有请求来了,就会调用处理请求的函数,直到函数返回,才会等待并处理下一个事件。 假如你的函数中 await 了别的异步事件,比如这样: async function() { await sleep(10); send('ok'); } 可以把 async function 看成回调函数的语法糖,实际的逻辑类似: function() { sleep(10, function() { send('ok'); }); } await 时当前函数就返回了,继续等待事件,此时就可以处理其他请求了。sleep 完成后也会有个 sleep 完成的事件,会调用传递给 sleep 的回调函数,看上去就是 await 完成了继续执行。 |
8
star7th 2022-11-16 12:06:18 +08:00
@no13bus 是这个 worker_threads https://juejin.cn/post/7062733724504293413
|
9
himself65 2022-11-16 12:16:50 +08:00 via iPhone 4
作为 nodejs member 补充一下个人看法,nodejs 底层是包装了 libuv ,对于用户(开发者)来说是单线程,但是底层可能会给不同的线程处理你的异步请求(比如读文件)
比如随时找了个文章: https://www.digitalocean.com/community/tutorials/how-to-use-multithreading-in-node-js# |
10
otakustay 2022-11-16 12:39:45 +08:00
不用 cluster 的话,请求里的代码逻辑依然是单线程的,即一个请求在 CPU 处理(非 IO )时,另一个请求要 CPU 是会卡住的
|
11
okakuyang 2022-11-16 13:01:14 +08:00
请求可以接收很多,但是只能一个个处理,因为 node 是单线程。也可以用 node 的多线程,这样看起来就像有多个 node 分别处理请求。
|
12
dcsuibian 2022-11-16 13:06:28 +08:00 1
就是单线程的,每个时刻只能处理一个请求。(不包括 worker )
但关键在于,CPU 的速度远远快于 IO ,时间往往浪费在 IO 等待而不是程序运行上。 按我的理解,Nodejs 就是把这种 IO 操作交给了底层,底层可以是多线程的,总之你别管。 所以如果是 IO 密集型应用,那么 Nodejs 是非常合适的,但对于计算密集型应用,就确实会卡住了。 |
13
ochatokori 2022-11-16 13:21:15 +08:00 via Android
请求对 node 来说也是异步的 io
|
14
cctv1005s927 2022-11-16 16:56:34 +08:00
Linux 上是 epoll 啦: https://kaleid-liner.github.io/blog/2019/06/02/epoll-web-server.html
再进入 internal function 之前的所有 JS 代码都在单线程上跑着,然后进入 internal function 之后就把 http 请求交给 epoll 去处理了。 |
15
dudubaba 2022-11-16 17:09:20 +08:00 1
请求是异步,执行是同步。假如 1000 个请求一起进来,然后代码里给个超时任务,那所有的请求都会被挂起。所以 node 里一般不做同步操作磁盘或者计算等操作。
|
16
Jamy 2022-11-16 17:12:47 +08:00 1
执行 js 代码的线程是只有一个,所有需要 js 处理的操作都会进入一个队列,node 按照特定的规则处理队列数据,
当涉及到 io 时会使用操作系统提供的 io 复用接口或者新开线程特殊处理,处理完成再把结果扔回队列 |
17
nielinjie 2022-11-16 17:19:05 +08:00
楼主要搞清楚的是单线程 /多线程、同步 /异步、阻塞 /非阻塞这几个概念。这几个有关系但不相同。
|
18
Yeen 2022-11-16 17:28:03 +08:00
首先要清楚,nodejs 也是基于 js 语言模式,因此主线程也是 event loop 的。
网上讲这个资料的很多可搜一下。 每次由事件触发一次处理,进入 /退出事件处理代码(就是用户代码)。 async/await 无非就是某次进入 event loop 的代码执行显式的停留在等待 promise 的 resovle/reject 的调用点,直到 promise 已决才继续往下执行,注意只是代码执行停留,但线程不阻塞。此时主线程完全可能进入其他的 event loop 处理,(比如其他地方定时器唤醒),因此是异步的。 |
19
coolloves 2022-11-16 17:37:30 +08:00
cy 慢慢看
|
20
KouShuiYu 2022-11-16 21:16:18 +08:00
我的理解只要不是调用了同步方法各种 xxxSync 或者 CPU 在一直计算比如从 1 循环加到一亿,都可以
你可以试试 先访问 http://127.0.0.1:3000/?t=10000 再访问 http://127.0.0.1:3000/?t=0 第一次结果还没返回,第二次结果是秒回的 const http = require('http'); const { URL } = require('url'); const { setTimeout } = require('timers/promises'); const hostname = '127.0.0.1'; const port = 3000; const server = http.createServer(async (req, res) => { const t = new URL(`http://${hostname}:${port}${req.url}`).searchParams.get('t'); // 模拟耗时操作 await setTimeout(t); res.end(`Hello World:${t}`); }); server.listen(port, hostname, () => { console.log(`Server running at http://${hostname}:${port}`); }); |
22
XCFOX 2022-11-17 00:01:40 +08:00
|
23
DICK23 2022-11-17 10:15:17 +08:00
异步单线程,各种任务处理可以看成是异步任务的注册,等任务完成再通知主线程进行后续处理
|