【问题标题】:Race Condition when calling node.js service from client从客户端调用 node.js 服务时的竞争条件
【发布时间】:2012-12-18 17:59:21
【问题描述】:

我有两个节点服务调用:一个是从 LDAP 组中获取组成员,另一个是获取每个成员的详细信息。每次调用都不会阻塞回调,所以我遇到了一种情况,有时我正在使用的记录与为个人检索的详细信息不匹配。

有没有一种聪明的方法来编写代码来防止竞争条件?有时它可以工作,但通常在我在下面进行控制台日志的行上,我正在迭代的记录与从商店返回以获取员工详细信息的记录不匹配。下面看一下代码:

onGroupSelected: function (list, idx, el, record) {
    var store = Ext.getStore('GroupMembers');
    store.getProxy().setUrl('http://people-nodejs.xxx.xxx.com/ldap/group/' + record.get('cn'));
    var that = this;
    store.load({
        callback: function (records, operation) {
            var empstore = Ext.getStore('EmployeeDetails');
            Ext.each(records, function(rec, ndx) {
                console.log(ndx + ":" + rec.get('memberUid'));
                if (rec.get('isMember')) {
                    empstore.getProxy().setUrl('http://people-nodejs.xxx.xxx.com/ldap/user/' + rec.get('memberUid'));
                    empstore.load({
                        callback: function(emps, op) {
                            console.log(ndx + ":" + rec.get('memberUid') + "--" + emps[0].get('fullName'));
                            rec.set('workforceID', emps[0].get('workforceID'));
                            rec.set('fullName', emps[0].get('fullName'));
                            that.getGroupMembersList().setRecord(rec);
                        }
                    });
                }
            });
            // Remove empty records and populate with records from above
            store.removeAll();
        }
    });

【问题讨论】:

    标签: javascript node.js asynchronous


    【解决方案1】:

    在单线程环境中,比如 Javascript(和 node.js),你不能真正有竞争条件。

    相反,您遇到了 javascript 闭包,并且对变量在嵌套函数和循环中的工作方式存在误解。

    recExt.each(records, function(rec, ndx) 内部定义,在你的回调函数内部,你正在使用它的值,假设对于每次迭代,当回调时 rec 仍然绑定到相同的值被处决了。

    这是不正确的,而是rec 的值是在回调函数执行时绑定的,而不是在函数创建时绑定的(这实质上是闭包的定义)。

    因此,要让它按您期望的方式工作,您需要在创建期间强制绑定 rec,方法是执行以下操作(注意:语法可能有问题):

    callback: (function(myrec,index){ 
        return function(emps, op) {
          console.log(index + ":" + myrec.get('memberUid') + "--" + emps[0].get('fullName'));
          myrec.set('workforceID', emps[0].get('workforceID'));
          myrec.set('fullName', emps[0].get('fullName'));
          that.getGroupMembersList().setRecord(myrec);
        };)(rec,ndx);
    

    在上面的示例中,我们立即执行了一个匿名 javascript 函数,传入值 rec,该值在创建时被 myrec 绑定。匿名函数返回一个与回调所期望的签名相同的函数,当你的回调执行该函数时,它将具有rec的正确值

    【讨论】:

    • 浏览器客户端不是单线程的,因为它同时调用 node.js 服务以获取 emps 详细信息。不过,您提出了一个很好的观点;但是,即使在更正关闭之后,此更改仍然会产生相同的用户被复制的结果。 :(
    • 客户端对服务器的每次调用都应该由节点“自包含”,就好像它是在自己的线程上执行的一样,除非您在处理程序中使用全局变量。如果您看到由对服务器 api 的多次调用引起的问题,那么我怀疑您在调用堆栈上还有另一个与闭包相关的问题(可能在调用 onGroupSelected 的代码中)?
    • 或者这个代码:var empstore = Ext.getStore('EmployeeDetails'); Javascript 的事件队列将同步执行,直到它等待进一步的 IO(并被放回事件队列)。所以很有可能empstore 被同一个用户加载了两次。我会为此使用双重检查解决方案。从本质上看,在加载之前,empstore 是否已经包含您要加载的记录。
    • 我查看了堆栈,没有其他回调。 empstore 一次只能包含一条记录(它从以前的商店搜索每个员工——组成员),所以我所做的是将这一功能隔离到一个单独的函数中,并检查是否 emps.uid == = myrec.uid。如果没有,我会进行递归调用以重试。我意识到很难看,但节点速度太快了,你甚至无法从 UI 中分辨出来。目前我找不到更好的解决方案。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-09-01
    • 1970-01-01
    • 2018-08-13
    • 2017-09-17
    相关资源
    最近更新 更多