简单的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 相关文章推荐
DEFER怎么用?
Jul 01 Javascript
prototype.js的Ajax对象
Sep 23 Javascript
制作高质量的JQuery Plugin 插件的方法
Apr 20 Javascript
js中关于new Object时传参的一些细节分析
Mar 13 Javascript
location对象的属性和方法应用(解析URL)
Apr 12 Javascript
javascript父子页面通讯实例详解
Jul 17 Javascript
js clearInterval()方法的定义和用法
Nov 11 Javascript
jQuery自定义图片缩放拖拽插件imageQ实现方法(附demo源码下载)
May 27 Javascript
基于jQuery实现顶部导航栏功能
Dec 27 Javascript
老生常谈的跨域处理
Jan 11 Javascript
AngularJS实现页面跳转后自动弹出对话框实例代码
Aug 02 Javascript
Javascript通过控制类名更改样式
May 24 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 object转数组示例
2014/01/15 PHP
求帮忙修改个php curl模拟post请求内容后并下载文件的解决思路
2015/09/20 PHP
PHP设计模式之单例模式原理与实现方法分析
2018/04/25 PHP
深入浅析安装PhpStorm并激活的步骤详解
2020/09/17 PHP
THINKPHP5分页数据对象处理过程解析
2020/10/28 PHP
基于Jquery的将DropDownlist的选中值赋给label的实现代码
2011/05/06 Javascript
javascript学习笔记(五) Array 数组类型介绍
2012/06/19 Javascript
分享一道笔试题[有n个直线最多可以把一个平面分成多少个部分]
2012/10/12 Javascript
使用node.js半年来总结的 10 条经验
2014/08/18 Javascript
javascript 应用小技巧方法汇总
2015/07/05 Javascript
深入解析AngularJS框架中$scope的作用与生命周期
2016/03/05 Javascript
DropDownList实现可输入可选择(两种版本可选)
2016/12/07 Javascript
Bootstrap和Java分页实例第一篇
2016/12/23 Javascript
关于Vue项目跨平台运行问题的解决方法
2018/09/18 Javascript
vue中使用cookies和crypto-js实现记住密码和加密的方法
2018/10/18 Javascript
vue 获取视频时长的实例代码
2019/08/20 Javascript
使用webpack搭建pixi.js开发环境
2020/02/12 Javascript
node脚手架搭建服务器实现token验证的方法
2021/01/20 Javascript
简介Python中用于处理字符串的center()方法
2015/05/18 Python
Python cookbook(字符串与文本)在字符串的开头或结尾处进行文本匹配操作
2018/04/20 Python
深入浅析Python中list的复制及深拷贝与浅拷贝
2018/09/03 Python
Python通过paramiko远程下载Linux服务器上的文件实例
2018/12/27 Python
Python XlsxWriter模块Chart类用法实例分析
2019/03/11 Python
python将字典列表导出为Excel文件的方法
2019/09/02 Python
python实现ssh及sftp功能(实例代码)
2020/03/16 Python
python实现测试工具(一)——命令行发送get请求
2020/10/19 Python
CSS3 选择器 伪类选择器介绍
2012/01/21 HTML / CSS
amazeui页面分析之登录页面的示例代码
2020/08/25 HTML / CSS
澳大利亚网上书店:QBD
2021/01/09 全球购物
房产转让协议书
2014/04/11 职场文书
实习生工作证明范本
2014/09/14 职场文书
2014年药店店长工作总结
2014/11/17 职场文书
工程竣工验收申请报告
2015/05/15 职场文书
劳动争议仲裁代理词
2015/05/25 职场文书
解决ObjectMapper.convertValue() 遇到的一些问题
2021/06/30 Java/Android
SQL Server2019安装的详细步骤实战记录(亲测可用)
2022/06/10 SQL Server