我对节点js和socket io很新。此代码是否可以导致计数器变量的竞争条件。我应该使用锁定库来安全地更新计数器变量。
"use strict";
module.exports = function (opts) {
var module = {};
var io = opts.io;
var counter = 0;
io.on('connection', function (socket) {
socket.on("inc", function (msg) {
counter += 1;
});
socket.on("dec" , function (msg) {
counter -= 1;
});
});
return module;
};
答案 0 :(得分:7)
不,这里没有竞争条件。 node.js中的Javascript是单线程和事件驱动的,因此一次只执行一个socket.io事件处理程序。这是来自单线程模型的良好编程简化之一。它运行一个给定的执行线程,然后才会从事件队列中获取下一个事件并运行它。
希望您确实意识到所有socket.io连接都访问了相同的counter
变量。虽然这不是竞争条件,但这意味着所有socket.io连接只能修改一个counter
。
如果你想要一个每连接计数器(每个连接的separeate计数器),那么你可以在counter
处理程序中定义io.on('connection', ....)
变量。
您必须在node.js中注意的竞争条件是当您进行异步调用然后在异步回调中继续其余的编码逻辑时。当异步操作正在进行时,其他node.js代码可以运行,并且可以更改您可能正在使用的可公开访问的变量。在你的counter
示例中并非如此,但它确实发生在许多其他类型的node.js编程中。
例如,这可能是一个问题:
var flag = false;
function doSomething() {
// set flag indicating we are in a fs.readFile() operation
flag = true;
fs.readFile("somefile.txt", function(err, data) {
// do something with data
// clear flag
flag = false;
});
}
在这种情况下,在我们调用fs.readFile()
之后,我们立即将控制权返回给node.js.当时可以免费运行其他操作。如果另一个操作也可以运行此代码,那么它将在flag
的值上进行操作,并且我们会遇到并发问题。
因此,您必须注意,无论何时进行异步操作,然后其余逻辑都会继续执行异步操作的回调,其他代码可以运行,并且此时可以访问任何共享变量。您需要制作共享数据的本地副本,或者需要为共享数据提供适当的保护。
在这种特殊情况下,标志可以递增和递减,而不是简单地设置为true
或false
,它可能会用于跟踪此文件是否是当前正在读取的所需目的或不。
答案 1 :(得分:0)
更简短的答案:
“种族条件” 是当您执行一系列有序的异步函数时,由于它们的异步特性,它们将无法按其原始顺序完成处理。
在您的代码中,您正在执行一系列有序的同步过程(增加或减少计数器),因此它们在启动后立即完成,从而产生有序的输出。所以这里没有比赛!