zoukankan      html  css  js  c++  java
  • 简单的JavaScript互斥锁

    去年有几个项目需要使用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);
    View Code

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

    • 锁与解锁
    • 等待队列
    • 执行方法

    以上锁的用法:

    //定义锁的名称
    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进行解锁,就可以防止重复释放了。不过上面这段老代码中没有,估计很快就会用到的了。

  • 相关阅读:
    HTTP Header 详解
    面试题----网页/应用访问慢突然变慢,如何定位问题
    PHP实现斐波那契数列
    常见的HTTP返回状态值
    通过实例理解单列索引、多列索引以及最左前缀原则
    Btree索引和Hash索引
    遍历和删除文件夹
    面试题之----禁掉cookie的session使用方案
    面试题之----写个函数来解决多线程同时读写一个文件的问题
    heredoc
  • 原文地址:https://www.cnblogs.com/indream/p/3537210.html
Copyright © 2011-2022 走看看