阻塞与非阻塞

  2016-12-11 15:07

原文:Overview of Blocking vs Non-Blocking

这是一篇关于Node.js“阻塞”与“非阻塞”调用的概述文章。这篇文章会涉及到事件循环与libuv,但并不需要深入的掌握。我们假设读者对JavaScript语言及Node.js回调特性有基本的了解。

阻塞

“阻塞”是指Node.js进程中附加的JavaScript必须要等到非JavaScript操作完成之后方可执行。这是因为在当阻塞性操作发生时,事件循环无法继续运行JavaScript。

在Node.js中,由于CPU集增使JavaScript表现得性能低下而不是等到非JavaScript后操作执行(例如I/O)并不是典型的“阻塞”。Node.js标准库中使用的libuv的同步方法是最常见使用阻塞操作的。本地模块中也有使用阻塞方法的。

Node.js标准库中所有的I/O方法都提供异步版本,也就是非阻塞的,接受回调函数。一些方法含有阻塞的副本,以Sync结尾命名。

代码比较

阻塞方法是同步执行的,而非阻塞方法是异步执行的。
用文件管理模块作为例子,这是一个同步方法:

const fs = require('fs');
const data = fs.readFileSync('/file.md');

下面是等价的异步方法:

const fs = require("fs");
fs.readFile('/file.md',(err,data) => {
  if(err) throw err;
});

第一种方法看起来比第二种方法简单,但是第二行的代码将阻止其他的JavaScript操作运行直至全部文件被读取完毕。注意在同步方法中,如果抛出错误需要接收,不然进程将崩溃掉。但在异步版本中,错误是否被抛出显示取决于作者。

让我们再丰富一下这个例子:

const fs = require('fs');
const data = fs.readFileSync('/file.md');
console.log(data);
// moreWork(); will run after console.log

下面是相似但并不等价的异步代码:

const fs = require('fs');
fs.readFile('/file.md',(err,data) => {
  if(err) throw err;
  console.log(data)
});
// moreWork(); will run before console.log

上面第一个例子里,console.log将在moreWork()之前调用。在第二个例子里,fs.readFile()是非阻塞性,JavaScript代码可以继续执行,moreWork()将被提前调用。这种不用等待文件读取完毕而调用moreWork()的能力是一种提升吞吐量的重要设计办法。

并行方法及吞吐量

Node.js中JavaScript的执行是单线程的,因而事件流中的并行是在完成其他工作之后再执行回调函数。任何将在并行状态下运行的代码必须允许事件流在非JavaScript操作(例如I/O)出现时继续运行。

举个例子,假如每个网络服务器请求需要50毫秒完成,期中的45毫秒是异步操作的数据库I/O处理。使用非阻塞性异步操作将释放每个请求中的45毫秒来执行其他请求。这是阻塞性方法与非阻塞性方法容量上的标志性不同。

事件流与其他语言中的并行工作采用额外线路执行的方法是有区别的。

混合使用阻塞与非阻塞性代码的危险

一些代码书写形式应该在I/O处理时尽量避免,举个例子:

const fs = require('fs');
fs.readFile('/file.md',(err,data) => {
  if(err) throw err;
  console.log(data);
});
fs.unlinkSync('/file.md');

上面的例子中,fs.unlinkSync()可能在fs.readFile()之前运行,这将使file.md在实际读取前就被删掉了。更好的办法是完全使用非阻塞性代码保证正确的执行顺序:

const fs = require('fs');
fs.readFile('/file.md', (err, data) => {
  if (err) throw err;
  console.log(data);
  fs.unlink('/file.md', (err) => {
    if (err) throw err;
  });
});

上面将非阻塞性调用放在了fs.unlink()前接收fs.readFile()的回调函数,保证了操作的正确顺序。

你好,游客!(点击更改信息)

您的电子邮件不会被公布,带*为必填。


  •   正在提交中,请稍候...
      评论提交成功
    回复 的评论,点击取消回复。