简单的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 相关文章推荐
js动态创建表格,删除行列的小例子
Jul 20 Javascript
通过一段代码简单说js中的this的使用
Jul 23 Javascript
js实现连续英文字符自动换行兼容ie6 ie7和firefox
Sep 06 Javascript
php跨域调用json的例子
Nov 13 Javascript
javaScript array(数组)使用字符串作为数组下标的方法
Nov 19 Javascript
使用jQuery实现验证上传图片的格式与大小
Dec 03 Javascript
javascript判断数组内是否重复的方法
Apr 21 Javascript
js基本算法:冒泡排序,二分查找的简单实例
Oct 08 Javascript
详解ES6中的三种异步解决方案
Jun 28 Javascript
微信小程序用户位置权限的获取方法(拒绝后提醒)
Nov 15 Javascript
Vue CLI2升级至Vue CLI3的方法步骤
May 20 Javascript
react+antd 递归实现树状目录操作
Nov 02 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
php目录管理函数小结
2008/09/10 PHP
PHP输出数组中重名的元素的几种处理方法
2012/09/05 PHP
php cc攻击代码与防范方法
2012/10/18 PHP
PHP多线程之内部多线程实例分析
2015/03/09 PHP
PHP上传图片时判断上传文件是否为可用图片的方法
2016/10/20 PHP
php 微信开发获取用户信息如何实现
2016/12/13 PHP
PHP封装的完整分页类示例
2018/08/21 PHP
Mozilla 表达式 __noSuchMethod__
2009/04/05 Javascript
基于JavaScript 下namespace 功能的简单分析
2013/07/05 Javascript
JS分页效果示例
2013/10/11 Javascript
浅谈javascript构造函数与实例化对象
2015/06/22 Javascript
jQuery实现文件上传进度条特效
2015/08/12 Javascript
js获取鼠标位置实例详解
2015/12/09 Javascript
利用JS判断字符串是否含有数字与特殊字符的方法小结
2016/11/25 Javascript
基于Bootstrap的网页设计实例
2017/03/01 Javascript
js禁止浏览器的回退事件
2017/04/20 Javascript
深入学习nodejs中的async模块的使用方法
2017/07/12 NodeJs
javaScript中的空值和假值
2017/12/18 Javascript
JavaScript中Object值合并方法详解
2017/12/22 Javascript
详解关于Vuex的action传入多个参数的问题
2019/02/22 Javascript
ES6中Symbol、Set和Map用法详解
2019/08/20 Javascript
详解React中共享组件逻辑的三种方式
2021/02/02 Javascript
编写Python爬虫抓取豆瓣电影TOP100及用户头像的方法
2016/01/20 Python
windows下安装python的C扩展编译环境(解决Unable to find vcvarsall.bat)
2018/02/21 Python
Python实现的redis分布式锁功能示例
2018/05/29 Python
python 字典中取值的两种方法小结
2018/08/02 Python
详解Django+uwsgi+Nginx上线最佳实战
2019/03/14 Python
Wilson体育用品官网:美国著名运动器材品牌
2019/05/12 全球购物
精选奢华:THE LIST
2019/09/05 全球购物
无偿献血倡议书
2014/04/14 职场文书
宣传普通话标语
2014/06/27 职场文书
优秀三好学生事迹材料
2014/08/31 职场文书
学生违纪检讨书200字
2014/10/21 职场文书
终止劳动合同通知书
2015/04/16 职场文书
学校财务管理制度
2015/08/04 职场文书
关于CSS自定义属性与前端页面的主题切换问题
2022/03/21 HTML / CSS