简单的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实现的网站首页随机公告随机公告
Mar 14 Javascript
javascript dom代码应用 简单的相册[firefox only]
Jun 12 Javascript
基于jquery的下拉框改变动态添加和删除表格实现代码
Sep 12 Javascript
JavaScript sub方法入门实例(把字符串显示为下标)
Oct 17 Javascript
jquery实现浮动在网页右下角的彩票开奖公告窗口代码
Sep 04 Javascript
jQuery Validate表单验证插件 添加class属性形式的校验
Jan 18 Javascript
通过扫描二维码打开app的实现代码
Nov 10 Javascript
JS实现中国公民身份证号码有效性验证
Feb 20 Javascript
vue自动化表单实例分析
May 06 Javascript
JS实现显示当前日期的实例代码
Jul 03 Javascript
使用ThinkJs搭建微信中控服务的实现方法
Aug 08 Javascript
Vue使用mixin分发组件的可复用功能
Sep 01 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 第三节 变量介绍
2012/04/28 PHP
解析posix与perl标准的正则表达式区别
2013/06/17 PHP
浅谈php安全性需要注意的几点事项
2014/07/17 PHP
php使用Jpgraph绘制复杂X-Y坐标图的方法
2015/06/10 PHP
Symfony2针对输入时间进行查询的方法分析
2017/06/28 PHP
PHP实现对数字分隔加千分号的方法
2019/03/18 PHP
JavaScript高级程序设计 阅读笔记(十三) js定义类或对象
2012/08/14 Javascript
JS控制输入框内字符串长度
2014/05/21 Javascript
利用jsonp跨域调用百度js实现搜索框智能提示
2016/08/24 Javascript
[js高手之路]图解javascript的原型(prototype)对象,原型链实例
2017/08/28 Javascript
利用JS hash制作单页Web应用的方法详解
2017/10/10 Javascript
解决vue中修改export default中脚本报一大堆错的问题
2018/08/27 Javascript
Vue开发环境跨域访问问题
2020/01/22 Javascript
javascript设计模式 ? 桥接模式原理与应用实例分析
2020/04/13 Javascript
详解element-ui 表单校验 Rules 配置 常用黑科技
2020/07/11 Javascript
Vue切换组件实现返回后不重置数据,保留历史设置操作
2020/07/21 Javascript
Python Requests 基础入门
2016/04/07 Python
Python os.rename() 重命名目录和文件的示例
2018/10/25 Python
在Pandas中给多层索引降级的方法
2018/11/16 Python
Pycharm 文件更改目录后,执行路径未更新的解决方法
2019/07/19 Python
Python实现寻找回文数字过程解析
2020/06/09 Python
Python中免验证跳转到内容页的实例代码
2020/10/23 Python
Django 权限管理(permissions)与用户组(group)详解
2020/11/30 Python
python绕过图片滑动验证码实现爬取PTA所有题目功能 附源码
2021/01/06 Python
python爬虫scrapy框架的梨视频案例解析
2021/02/20 Python
Rag & Bone官网:瑞格布恩高级成衣
2018/04/19 全球购物
德国珠宝和手表在线商店:VALMANO
2019/03/24 全球购物
施华洛世奇西班牙官网:SWAROVSKI西班牙
2019/06/06 全球购物
军用级手机壳,专为冒险而建:Zizo Wireless
2019/08/07 全球购物
优秀中学生事迹材料
2014/01/31 职场文书
2014年基层党组织公开承诺书
2014/03/29 职场文书
《闻一多先生的说和做》教学反思
2014/04/28 职场文书
山东省召开党的群众路线教育实践活动总结大会新闻稿
2014/10/21 职场文书
党的群众路线专项整治方案
2014/11/03 职场文书
Go 语言中 20 个占位符的整理
2021/10/16 Golang
Python图像处理库PIL详细使用说明
2022/04/06 Python