简单的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 相关文章推荐
javascript 放大镜效果js组件 qsoft.PopBigImage.v0.35 加入了chrome支持
Apr 07 Javascript
使用jQuery向asp.net Mvc传递复杂json数据-ModelBinder篇
May 07 Javascript
js判断运行jsp页面的浏览器类型以及版本示例
Oct 30 Javascript
jquery自动将form表单封装成json的具体实现
Mar 17 Javascript
Javascript实现简单的富文本编辑器附演示
Jun 16 Javascript
javascript性能优化之事件委托实例详解
Dec 12 Javascript
[原创]JavaScript语法高亮插件highlight.js用法详解【附highlight.js本站下载】
Nov 01 Javascript
详解React Native网络请求fetch简单封装
Aug 10 Javascript
深入理解ES7的async/await的用法
Sep 09 Javascript
原生JS实现轮播图效果
Oct 12 Javascript
微信小程序实现锚点功能
Nov 20 Javascript
微信小程序获取公众号文章列表及显示文章的示例代码
Mar 10 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生成制作验证码代码详解(推荐)
2016/06/12 PHP
基于php(Thinkphp)+jquery 实现ajax多选反选不选删除数据功能
2017/02/24 PHP
php封装单文件上传到数据库(路径)
2017/10/15 PHP
thinkPHP中钩子的使用方法实例分析
2017/11/16 PHP
php进程daemon化的正确实现方法
2018/09/06 PHP
PHP 进程池与轮询调度算法实现多任务的示例代码
2019/11/26 PHP
不错的一个日期输入 动态
2006/11/06 Javascript
用js实现键盘方向键翻页功能的代码
2007/06/03 Javascript
JavaScript自执行闭包的小例子
2013/06/29 Javascript
jQuery中prev()方法用法实例
2015/01/08 Javascript
js实现tab选项卡切换功能
2017/01/13 Javascript
Javascript中 带名 匿名 箭头函数的重要区别(推荐)
2017/01/29 Javascript
easyui-edatagrid.js实现回车键结束编辑功能的实例
2017/04/12 Javascript
使用travis-ci如何持续部署node.js应用详解
2017/07/30 Javascript
nodejs实现解析xml字符串为对象的方法示例
2018/03/14 NodeJs
vue element中axios下载文件(后端Python)
2019/05/10 Javascript
vue 2.5.1 源码学习 之Vue.extend 和 data的合并策略
2019/06/04 Javascript
vue日历/日程提醒/html5本地缓存功能
2019/09/02 Javascript
js里面的变量范围分享
2020/07/18 Javascript
Node.js web 应用如何封装到Docker容器中
2020/09/01 Javascript
[44:50]2018DOTA2亚洲邀请赛 4.1 小组赛 A组 TNC vs VG
2018/04/02 DOTA
Python实现的数据结构与算法之链表详解
2015/04/22 Python
Python实现Smtplib发送带有各种附件的邮件实例
2017/06/05 Python
Django Rest framework之认证的实现代码
2018/12/17 Python
python爬虫 爬取58同城上所有城市的租房信息详解
2019/07/30 Python
基于Python批量生成指定尺寸缩略图代码实例
2019/11/20 Python
AmazeUI 网格的实现示例
2020/08/13 HTML / CSS
合伙协议书
2014/04/23 职场文书
工程造价专业求职信
2014/07/17 职场文书
青年志愿者活动方案
2014/08/17 职场文书
党员学习新党章思想汇报
2014/10/25 职场文书
2014年小学语文工作总结
2014/12/20 职场文书
写给导师的自荐信
2015/03/06 职场文书
MySQL InnoDB ReplicaSet(副本集)简单介绍
2021/04/24 MySQL
浅谈Laravel中使用Slack进行异常通知
2021/05/29 PHP
教你在 Java 中实现 Dijkstra 最短路算法的方法
2022/04/08 Java/Android