node.js代码是否会导致竞态条件?

从我读到的内容来看,当不同线程尝试更改一个共享variables时会出现竞争条件,这可能会导致一个值,这些线程执行的任何串行顺序都是不可能的。

但是node.js中的代码运行在单个线程中,那么这是否意味着在node.js中编写的代码没有竞争条件?

不,这是真的,你不能在一个单线程, 非I / O程序的竞争条件。

但是node.js由于其非阻塞的编程方式而主要是快速的。 非阻塞意味着将侦听器设置为响应事件,您可以在等待此响应时执行其他操作。

为什么? 因为获取响应的工作是在另一个线程上完成的。 数据库,文件系统,在其他线程上运行,客户端显然运行在另一台计算机上,你的程序工作stream可以依赖于它的响应。

所以严格来说,node.js运行在一个线程上,但是你的程序工作stream,包括I / O(数据库,文件系统),客户端和一切,都运行在multithreading上。

因此,如果您向数据库添加某项内容的请求,则仍然存在争用条件,然后只需发送一个请求即可将其删除,而无需等待第一个请求的响应。 如果数据库运行在与node.js相同的线程中,则不会有竞态条件,并且请求只是立即执行的函数调用。

是。 一旦开始共享资源,Node.js可能会遇到竞争条件。

我错误地以为你不能在Node.js中获得竞争条件,因为它是单线程性质的,但是只要你在节点之外使用一个共享资源(例如来自文件系统的一个文件)就可能进入竞争状态。 当我试图理解这个问题时,我在这个问题上发布了一个这个问题的例子: node.js readfile woes

Node.js与其他环境的区别在于,您只有一个JavaScript执行线程,因此只有一个JavaScript实例运行您的代码(与线程环境相反,在这个线程环境中,有很multithreading执行您的应用程序代码时间。)

不。Node.js没有竞争情况,会由上下文切换引起; 不过,您仍然可以编写一个node.js程序,以exception顺序发生的asynchronous事件导致不一致的状态。

例如,假设你有两个function。 第一个通过WebSocket发送消息,并在callback中保存回复。 第二个function删除所有保存的答复。 按顺序调用函数并不保证为空的消息列表。 在进行asynchronous编程时,考虑所有可能的事件sorting是非常重要的。

编辑:这是一些示例代码

var messages = []; ... io.sockets.on('connection', function (socket) { socket.emit('ask', { question: 'How many fish do you have?' }); socket.on('reply', function (data) { messages.push(data); }); ... wipe(); }); function wipe() { setTimeout(function() { messages = []; }, 500); } 

竞争条件仍然可以发生,因为它们与线程无关,而是对事件时序和顺序做出假设,所以线程就是其中的一个例子。

Node.js是单线程的,但仍然是并发的,竞争条件是可能的。 例如:

 var http = require('http'); var size; http.createServer(function (req, res) { size = 0; req.on('data', function (data) { size += data.length; }); req.on('end', function () { res.end(size.toString()); }) }).listen(1337, '127.0.0.1'); 

这个程序应该向客户发送他们请求的大小。 如果你testing它,似乎工作正确。 但它实际上是基于隐含的假设,在请求开始和结束事件之间什么都不发生。 如果有2个或更多的并发客户端,它将无法工作。

这是因为sizevariables是共享的,就像两个线程共享一个variables一样。 你可以想象一个抽象的“asynchronous的上下文”,这很像线程,但它只能在某些点暂停。

是。 它可以。

使用cluster模块初始化多个工作人员时,Nodejs中的争用条件是可行的。

案子

 var cluster = require('cluster'); var fs = require('fs'); if(cluster.isMaster){ for(var i=0;i<4;i++){ cluster.fork(); } }else{ fs.watch('/path/to/file',function(){ var anotherFile = '/path/to/anotherFile'; fs.readFile(anotherFile,function(er,data){ if(er){ throw er; } data = +data+1; fs.writeFile(anotherFile,data,function(er){ if(er){ throw er; } fs.readFile(anotherFile,function(er,newData){ if(er){ throw er; } console.log(newData); //newData is now undetermined }); }); }); }); } 

每当您更改观看的文件时,4名工作人员将同时执行处理程序。 这种行为导致未确定的newData

解决scheme

 if(cluster.isMaster){ var lock = {}; var timer = setInterval(function(){ if(Object.keys(cluster.workers).length >= 4){ return clearInterval(timer); } //note that this lock won't 100% work if workers are forked at the same time with loop. cluster.fork().on('message',function(id){ var isLocked = lock[id]; if(isLocked){ return console.log('This task has already been handled'); } lock[id] = 1; this.send('No one has done it yet'); }); },100); }else{ process.on('message',function(){ //only one worker can execute this task fs.watch('/path/to/file',function(){ var anotherFile = '/path/to/anotherFile'; fs.readFile(anotherFile,function(er,data){ if(er){ throw er; } data = +data+1; fs.writeFile(anotherFile,data,function(er){ if(er){ throw er; } fs.readFile(anotherFile,function(er,newData){ if(er){ throw er; } console.log(newData); //newData is now determined }); }); }); }); }); //ask the master for permission process.send('watch'); } 

是的 ,由于事件的顺序,竞争条件(由于事件的顺序,共享资源的价值不一致)仍然可能发生在任何可能导致其他代码正在运行的暂停点上(线程在任意行 )例如这段完全是单线程的asynchronous代码:

 var accountBalance = 0; async function getAccountBalance() { // Suppose this was asynchronously from a database or something return accountBalance; }; async function setAccountBalance(value) { // Suppose this was asynchronously from a database or something accountBalance = value; }; async function increment(value, incr) { return value + incr; }; async function add$50() { var balance, newBalance; balance = await getAccountBalance(); newBalance = await increment(balance, 50); await setAccountBalance(newBalance); }; async function main() { var transaction1, transaction2; transaction1 = add$50(); transaction2 = add$50(); await transaction1; await transaction2; console.log('$' + await getAccountBalance()); // Can print either $50 or $100 // which it prints is dependent on what order // things arrived on the message queue, for this very simple // dummy implementation it actually prints $50 because // all values are added to the message queue immediately // so it actually alternates between the two async functions }; main(); 

这个代码在每一个等待中都有一个暂停点,因此可以在不好的时间在两个函数之间切换,产生“$ 50”,而不是预期的“$ 100”,这与维基百科的线程中的Race Conditions的例子基本上是相同的例子,明确表示暂停/重新进入。

就像线程一样,尽pipe你可以用Lock(aka mutex)来解决这样的竞争条件。 所以我们可以像线程一样防止上面的竞争条件:

 var accountBalance = 0; class Lock { constructor() { this._locked = false; this._waiting = []; } lock() { var unlock = () => { var nextResolve; if (this._waiting.length > 0) { nextResolve = this._waiting.pop(0); nextResolve(unlock); }; if (this._waiting.length === 0) { this._locked = false; } }; if (this._locked) { return new Promise((resolve) => { this._waiting.push(resolve); }); } else { this._locked = true; return new Promise((resolve) => { resolve(unlock); }); } } } var account = new Lock(); async function getAccountBalance() { // Suppose this was asynchronously from a database or something return accountBalance; }; async function setAccountBalance(value) { // Suppose this was asynchronously from a database or something accountBalance = value; }; async function increment(value, incr) { return value + incr; }; async function add$50() { var unlock, balance, newBalance; unlock = await account.lock(); balance = await getAccountBalance(); newBalance = await increment(balance, 50); await setAccountBalance(newBalance); await unlock(); }; async function main() { var transaction1, transaction2; transaction1 = add$50(); transaction2 = add$50(); await transaction1; await transaction2; console.log('$' + await getAccountBalance()); // Now will always be $100 regardless }; main(); 

你可能有一些问题,如果你声明你的variables内的函数作为隐含全局variables而不是封闭的局部variables。

在这两个答案中(一个是我回答,另一个是我回答),你可以find一些你不希望遇到的问题的例子:

https://stackoverflow.com/questions/25229739/race-condition-and-common-mistakes

在调用它的函数的callback中使用参数