简单的JavaScript互斥锁分享


Posted in Javascript onFebruary 02, 2014

去年有几个项目需要使用JavaScript互斥锁,所以写了几个类似的,这是其中一个:

//Published by Indream Luo
//Contact: indreamluo@qq.com
//Version: Chinese 1.0.0
!function ($) {
    window.indream = window.indream || {};
    $.indream = indream;
    indream.async = {
        //
        //锁
        //lock: 锁的编号
        //action: 解锁后执行的方法
        //
        lock: function (lock, action) {
            $.indream.async.waitings[lock] = $.indream.async.waitings[lock] || [];
            $.indream.async.waitings[lock].push(action);
            //如果该锁未被使用,则当前action阻塞该锁
            if (!$.indream.async.lockStatus[lock] && action) {
                $.indream.async.lockStatus[lock] = true;
                if (arguments.length > 2) {
                    var args = 'arguments[2]';
                    for (var i = 3; i < arguments.length; i++) {
                        args += ', arguments[' + i + ']';
                    }
                    eval('$.indream.async.action.call(action, ' + args + ')');
                } else {
                    $.indream.async.action.call(action);
                }
            }
        },
        //
        //解锁
        //lock: 锁的编号
        //
        releaseLock: function (lock) {
            $.indream.async.waitings[lock].shift();
            //如果等待队列有对象,则执行等待队列,否则解锁
            if ($.indream.async.waitings[lock].length) {
                $.indream.async.waitings[lock][0]();
            } else {
                $.indream.async.lockStatus[lock] = false;
            }
        },
        //
        //锁的状态
        //
        lockStatus: [],
        //
        //等待事件完成
        //lock:锁编码,相同的编码将被整合成一个序列,触发时同时触发
        //
        wait: function (lock, action) {
            $.indream.async.waitings[code] = $.indream.async.waitings[code] || [];
            $.indream.async.waitings[code].push(action);
        },
        //
        //等待序列
        //
        waitings: [],
        //
        //数据缓存
        //
        action: {
            //
            //监听和回调的相关方法
            //
            callback: {
                //
                //监听
                //
                listen: function (actionName, callback) {
                    var list = $.indream.async.action.callback.list;
                    list[actionName] = list[actionName] || [];
                    list[actionName].push(callback);
                },
                //
                //回调
                //
                call: function (actionName, args) {
                    var list = $.indream.async.action.callback.list;
                    if (list[actionName] && list[actionName].length) {
                        for (var i in list[actionName]) {
                            $.indream.async.action.call(list[actionName][i], args);
                        }
                    }
                },
                //
                //现有的回调列表
                //
                list: []
            },
            //
            //根据方法是否存在和参数是否存在选择适当的执行方式
            //
            call: function (action) {
                if (action) {
                    if (arguments.length > 1) {
                        var args = 'arguments[1]';
                        for (var i = 2; i < arguments.length; i++) {
                            args += ', arguments[' + i + ']';
                        }
                        eval('action(' + args + ')');
                    } else {
                        action();
                    }
                }
            }
        }
    }
}(window.jQuery);

一个互斥锁的几个元素是:

•锁与解锁
•等待队列
•执行方法
以上锁的用法:

//定义锁的名称
var lock = 'scrollTop()';
//使用锁
$.indream.async.lock(lock, function () {
    var scrollTop = $(window).scrollTop();
    var timer;
    var fullTime = 100;
    for (timer = 0; timer <= fullTime; timer += 10) {
        setTimeout('$(window).scrollTop(' + (scrollTop * (fullTime - timer) / fullTime) + ');', timer);
    }
    //释放锁
    setTimeout('$.indream.async.releaseLock("' + lock + '");', fullTime);
});

关于这次所的实现,简单说明下。

-自旋锁还是信号量
JavaScript本身没有锁的功能,所以做的锁都是在高层实现的。

依据JavaScript单线程的原理,JS的线程资源十分有限,非常不适合使用自旋锁,所以选择了使用信号量。

自旋锁实现起来的样子大致是这样的,当然do while更多用了:

while(true) {
    //do something...
}

这样必然需要占满线程资源,可惜JS只有一条线程可以用来执行,所以这样做十分不适用。当然,有需要可以选择setInterval和clearInterval的组合去实现,效果也会不错。

这里选用了信号量的方式,原理也简单,就如代码那么短。工作的执行顺序大致是:

•把代码段(回调的action)推入等待队列
•判断当前锁是否被持有,如果被持有则等待释放,否则获取该锁,执行回调
•当锁被释放,则在等待队列中shift出下一个回调,将锁传递给它并执行
 

-自动释放还是手动释放
看起来最舒服的方式当然是锁住之后当当前程序执行完就自动释放,不过这样并不容易,因为有更多的情况需要自定义释放场景。

本身使用锁的就是在异步中的方法,所以各种通常也会出现其他异步内容,比如AJAX、jQuery动画。这个时候,自动释放就不符合需求了,因为实际上真正的“执行完毕”是在它内部的异步回调完成后,也就是基本上只有开发人员自己能把握,所以这里选择了手释放。

不过还是有缺陷的,就是重复释放。

可以看到所有的锁的对象都是公有的,或者应该说JS所有对象都是公有的,除非使局部变量在访问级别上进行隔离。不过这里“锁”本身就是个公共资源,所以没办法处理。

这里可以做的优化应该是像setInterval和clearInterval的那样,以公共的锁名称进行加锁,以私有的锁ID进行解锁,就可以防止重复释放了。不过上面这段老代码中没有,估计很快就会用到的了。

Javascript 相关文章推荐
JQuery上传插件Uploadify使用详解及错误处理
Apr 27 Javascript
jquery 学习之二 属性相关
Nov 23 Javascript
javascript游戏开发之《三国志曹操传》零部件开发(四)用地图块拼成大地图
Jan 23 Javascript
谈谈JavaScript中的几种借用方法
Aug 09 Javascript
Node.js发送HTTP客户端请求并显示响应结果的方法示例
Apr 12 Javascript
angular4自定义组件详解
Sep 28 Javascript
JS实现的抛物线运动效果示例
Jan 30 Javascript
vue实现模态框的通用写法推荐
Feb 26 Javascript
详解小程序原生使用ES7 async/await语法
Aug 06 Javascript
vue cli使用融云实现聊天功能的实例代码
Apr 19 Javascript
vue实现codemirror代码编辑器中的SQL代码格式化功能
Aug 27 Javascript
js实现简单五子棋游戏
May 28 Javascript
在百度知道团队中快速审批新成员的js脚本
Feb 02 #Javascript
基于jquery的simpleValidate简易验证插件
Jan 31 #Javascript
基于JQuery实现的图片自动进行缩放和裁剪处理
Jan 31 #Javascript
jquery为页面增加快捷键示例
Jan 31 #Javascript
通过pjax实现无刷新翻页(兼容新版jquery)
Jan 31 #Javascript
jquery 页面滚动到底部自动加载插件集合
Jan 31 #Javascript
基于JQuery实现滚动到页面底端时自动加载更多信息
Jan 31 #Javascript
You might like
我的群发邮件程序
2006/10/09 PHP
PHP容易被忽略而出错陷阱 数字与字符串比较
2011/11/10 PHP
destoon调用企业会员公司形象图片的实现方法
2014/08/21 PHP
PHP中strcmp()和strcasecmp()函数字符串比较用法分析
2016/01/07 PHP
php使用正则验证中文
2016/04/06 PHP
ThinkPHP的SAE开发相关注意事项详解
2016/10/09 PHP
PHP编程实现计算抽奖概率算法完整实例
2017/08/09 PHP
PHP中使用mpdf 导出PDF文件的实现方法
2018/10/22 PHP
jquery 指南/入门基础
2007/11/30 Javascript
基于jquery的一个OutlookBar类,动态创建导航条
2010/11/19 Javascript
javascript实现继承的简单实例
2015/07/26 Javascript
深入解读JavaScript中的Hoisting机制
2015/08/12 Javascript
AngularJs解决跨域问题案例详解(简单方法)
2016/05/19 Javascript
jQuery通用的全局遍历方法$.each()用法实例
2016/07/04 Javascript
ES6新特性之模块Module用法详解
2017/04/01 Javascript
微信小程序与php 实现微信支付的简单实例
2017/06/23 Javascript
基于Bootstrap框架菜鸟入门教程(推荐)
2017/09/17 Javascript
Nodejs模块载入运行原理
2018/02/23 NodeJs
vue + webpack如何绕过QQ音乐接口对host的验证详解
2018/07/01 Javascript
简单通过settimeout看javascript的运行机制
2019/05/10 Javascript
Vue使用lodop实现打印小结
2019/07/06 Javascript
VUE:vuex 用户登录信息的数据写入与获取方式
2019/11/11 Javascript
微信小程序修改数组长度的问题的解决
2019/12/17 Javascript
jquery制作的移动端购物车效果完整示例
2020/02/24 jQuery
安装Python的教程-Windows
2017/07/22 Python
Python机器学习之SVM支持向量机
2017/12/27 Python
PyCharm配置mongo插件的方法
2018/11/30 Python
对python 生成拼接xml报文的示例详解
2018/12/28 Python
python 通过麦克风录音 生成wav文件的方法
2019/01/09 Python
Python实现数据结构线性链表(单链表)算法示例
2019/05/04 Python
python的scipy实现插值的示例代码
2019/11/12 Python
TensorFlow实现从txt文件读取数据
2020/02/05 Python
美国从事品牌鞋类零售的连锁店:Famous Footwear
2016/08/25 全球购物
施华洛世奇巴西官网:SWAROVSKI巴西
2019/12/03 全球购物
各类场合主持词开场白范文集锦
2019/08/16 职场文书
python 管理系统实现mysql交互的示例代码
2021/12/06 Python