Nodejs express 怎么让线程不阻塞

Nodejs express 怎么让线程不阻塞

在 express 的路由那,

app.get(’/index’, function(req,res){res.send(‘aa’)}); app.get(’/list’, function(req,res){ var startTime = new Date().getTime(); while (new Date().getTime() < startTime + 10000); //实际上面这代码,是一段 cpu 密集型的操作,可能花几秒 res.send(‘bb’); });

当我先访问/ list ,再访问 /index,输出的aa 要等10秒 bb 输出后才输出。

express没有处理线程阻塞的东西么?如果这个要自己处理,那么代码应该怎么改呢?


7 回复

Node.js Express 如何避免线程阻塞

在使用 Node.js 和 Express 构建 Web 应用时,经常会遇到一些长时间运行的任务,比如 CPU 密集型操作。这些任务可能会阻塞事件循环,导致其他请求无法及时处理。为了解决这个问题,我们可以使用异步编程技术来避免阻塞主线程。

示例代码

假设我们有以下两个路由:

const express = require('express');
const app = express();

// 模拟一个 CPU 密集型操作
function cpuIntensiveTask() {
    return new Promise((resolve) => {
        setTimeout(() => {
            resolve("完成 CPU 密集型任务");
        }, 10000); // 模拟耗时 10 秒的操作
    });
}

app.get('/index', (req, res) => {
    res.send('aa');
});

app.get('/list', async (req, res) => {
    const startTime = new Date().getTime();
    const result = await cpuIntensiveTask();
    console.log(result);
    res.send('bb');
});

app.listen(3000, () => {
    console.log('Server is running on port 3000');
});

解释

  1. 使用 asyncawait:

    • 我们将 /list 路由的处理函数定义为 async 函数,这样就可以使用 await 关键字来等待异步操作完成。
  2. 使用 PromisesetTimeout:

    • cpuIntensiveTask 函数返回一个 Promise,并在 Promise 中使用 setTimeout 来模拟一个耗时 10 秒的操作。这样可以避免阻塞主线程,因为 setTimeout 是非阻塞的。
  3. 异步处理:

    • 当客户端请求 /list 时,Express 会立即返回 Promise 并继续处理其他请求。一旦 cpuIntensiveTask 完成,它会发送响应 'bb'

总结

通过使用 asyncawait 以及 Promise,我们可以确保长时间运行的任务不会阻塞主线程。这样可以提高应用的响应性和整体性能。对于更复杂的任务,还可以考虑使用 Worker Threads 或第三方库如 node-cpu-worker 来进一步优化性能。


setTimeout可以吧。

有米有具体点的代码。。。。。。

node是单线程的,当遇到cpu密集操作的时候会造成下面的代码不会执行或者服务器假死的情况,你可以让c++封装那些cpu密集操作给js的api让开发者调用,或者用node提供的异步机制来处理 推荐用process.nextTick 在下一个事件循环中执行 process.nextTick( function () {

while (new Date().getTime() < startTime + 10000); } ) res.send(‘xxoo’);

一下这段话来自nodejs 官网,由坛子里大牛翻译的 0.10.X版本后用setImmediate 会好点

在0.8以及之前, process.nextTick() 会在当前事件循环结束时调用,这样通常是会在I/O开始前被调用的。所以很多项目都会使用process.nextTick()让它晚点做,而在I/O之前,看上去这样是正确的。事实上在大负载的I/O情况下,nextTick可能工作不正常,出现线程竞争情况。所以在v0.10.0版本 process.nextTick() 会在js代码执行完成后调用,而不是写入事件循环,可以说变同步了。应该尽量避免使用 process.nextTick() 来做递归,如果非要这么做请使用 setImmediate 来代替。

大概懂的了。。。

当在 Express 路由中执行 CPU 密集型操作时,这些操作会阻塞事件循环,导致其他请求需要等待。为了解决这个问题,你可以使用异步操作或利用工作线程(Worker Threads)来避免阻塞主线程。

示例代码

这里我们使用 setTimeout 来模拟异步操作,并使用 Promise 包装它:

const express = require('express');
const app = express();

function asyncOperation() {
    return new Promise((resolve) => {
        setTimeout(() => {
            resolve('bb');
        }, 10000); // 模拟耗时10秒的操作
    });
}

app.get('/index', (req, res) => {
    res.send('aa');
});

app.get('/list', async (req, res) => {
    try {
        const result = await asyncOperation();
        res.send(result);
    } catch (error) {
        res.status(500).send('Internal Server Error');
    }
});

app.listen(3000, () => {
    console.log('Server is running on port 3000');
});

解释

  1. 异步操作:通过将 CPU 密集型操作包装在一个 Promise 中,我们可以使用 await 关键字来等待操作完成,而不会阻塞主线程。
  2. 路由处理
    • /index 路由直接返回字符串 ‘aa’。
    • /list 路由调用 asyncOperation 函数,并使用 await 等待结果。这使得 asyncOperation 可以在后台运行,而不会阻塞主线程。
  3. 错误处理:使用 try...catch 结构来捕获可能的错误并返回适当的响应。

注意事项

  • 如果 CPU 密集型操作非常复杂,可以考虑使用 Web Worker 或子进程(child_process)来进一步隔离计算任务。
  • 使用 Promiseasync/await 可以简化异步代码的编写,同时保持代码的可读性和可维护性。

这样修改后,当你访问 /list 时,/index 不会被阻塞,两个请求可以并发处理。

回到顶部