node.js代码可以导致竞争条件吗?

 steveukuk 发布于 2023-01-30 15:37

根据我的描述,当不同的线程尝试更改共享变量时会出现竞争条件,这可能导致这些线程的任何串行执行顺序都无法实现的值.

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

6 个回答
  • 是.一旦开始共享资源,Node.js就会遇到竞争条件.

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

    Node.js与其他环境的不同之处在于,您只有一个JavaScript执行线程,因此只有一个JavaScript实例运行您的代码(与线程环境相反,在线程环境中有许多线程同时执行您的应用程序代码时间.)

    2023-01-30 15:37 回答
  • 不,这是真的,你不能在单线程,非I/O执行程序上有竞争条件.

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

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

    严格来说,node.js在一个线程上运行,但是你的程序工作流程包括I/O(数据库,文件系统),客户端和所有东西,在许多线程上运行.

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

    2023-01-30 15:39 回答
  • 是。它可以。

    当您使用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

    解决方案

    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');
    }
    

    2023-01-30 15:40 回答
  • 没有.Node.js没有因上下文切换而引起的竞争条件; 但是,您仍然可以编写node.js程序,其中以意外顺序发生的异步事件会导致状态不一致.

    例如,假设您有两个功能.第一个通过WebSocket发送消息,并在回调中保存回复.第二个功能删除所有已保存的回复.按顺序调用函数不保证空消息列表.在进行异步编程时,考虑所有可能的事件排序很重要.

    编辑:这是一些示例代码

    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);
    }
    

    2023-01-30 15:40 回答
  • 是的,竞争条件(在由于事件顺序而具有不一致值的共享资源的意义上)仍然可以发生在任何可能导致其他代码运行的悬浮点的任何地方(线程在任何行上),采取例如,这段完全是单线程的异步代码:

    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美元",这与维基百科的线程竞争条件示例基本相同.有明确的暂停/重新进入点.

    就像线程一样,你可以用锁(又称互斥锁)来解决这种竞争条件.所以我们可以像线程一样阻止上述竞争条件:

    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);
                } else {
                    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();
    

    2023-01-30 15:40 回答
  • 竞争条件仍然可能发生,因为它们实际上与线程无关,而是在对事件时序和序列做出假设时,因此线程只是一个例子.

    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');
    

    该程序应该向客户发送其请求的大小.如果你测试它,似乎工作正确.但它实际上是基于隐式假设,即请求开始和结束事件之间没有任何反应.如果有两个或更多并发客户端,它将无法工作.

    这发生在这里因为size变量是共享的,就像两个线程共享变量时一样.你可以考虑一个抽象的"异步上下文",这很像线程,但它只能在某些点暂停.

    2023-01-30 15:42 回答
撰写答案
今天,你开发时遇到什么问题呢?
立即提问
热门标签
PHP1.CN | 中国最专业的PHP中文社区 | PNG素材下载 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有