同步、异步转化和任务执行

同步、异步转化和任务执行 正如动静是相对的概念,有了它们,世界才充满盎然生气;变和不变也是哲学上的对立统一,在代码的世界里也一样;同步异步呢?

首先,来粗略地看看同步和异步各自有些什么好处

同步的好处:

  • 1、同步流程对结果处理通常更为简单,可以就近处理。
  • 2、同步流程对结果的处理始终和前文保持在一个上下文内。
  • 3、同步流程可以很容易捕获、处理异常。
  • 4、同步流程是最天然的控制过程顺序执行的方式。

异步的好处:

  • 1、异步流程可以立即给调用方返回初步的结果。
  • 2、异步流程可以延迟给调用方最终的结果数据,在此期间可以做更多额外的工作,例如结果记录等等。
  • 3、异步流程在执行的过程中,可以释放占用的线程等资源,避免阻塞,等到结果产生再重新获取线程处理。
  • 4、异步流程可以等多次调用的结果出来后,再统一返回一次结果集合,提高响应效率。

接下来,我不妨说一些同步和异步互相转化的故事。

先来看看这一段代码:

setTimeout(function(){  
while(true){  
        alert("In");  
    }  
},0);  
while(true){  
    alert("Out");  
}  

它的输出应该是怎样的呢?你可以试一试。

不同浏览器下它的表现不同,有的浏览器下一直显示In的弹出框;有的浏览器下一直显示Out的弹出框;还有的浏览器下先显示一个Out,再不断显示In的弹出框。

那是不是可以这样理解:

上面的代码本意是想描述一个页面的JavaScript代码进行类似于并行线程的执行(setTimeout调用的方法,似乎就是一个异步执行的方法,它本意是不阻止主流程的执行的),可是实际运行的结果发现,原来浏览器运行JavaScript,所谓的异步,只是对开发人员和用户的一个欺骗,世界只是看起来这个样子——实际上,在JavaScript的世界里,其实根本就是“单线程”的嘛!

其实,这是无所谓欺骗的,就如同对于单CPU的机器来说,所谓的多进程,其实也只有一个CPU轮流执行队列里的指令。只是这个世界本来就是那么残酷,也许是我们都看错了……

同步Ajax和异步Ajax

Ajax通常都是异步的,同步的Ajax调用会将浏览器当前页面挂起,拒绝一切用户操作,直至响应到达:

var req = new  XMLHttpRequest();  
req.open("GET", url, true);  //true表示异步,false表示同步
req.onreadystatechange  =  callback;  
req.send();  

JavaScript的一个悲剧

在JavaScript中,没有一个方法可以让主线程休息一段时间(Java中有sleep和wait),也就是说,如果我想在某一个执行逻辑中,休息一会、等待一会,这样的实现都会变得很困难(Jscex就是用来解决这样的问题的)。这似乎是JavaScript的一个天生的重大缺陷。

Jscex带来的最大好处,就在于可以用同步的思维和编码,来解决异步的问题:

var moveAsync = eval(Jscex.compile("$async", function(e, startPos, endPos, duration) {  
    for (var t = 0; t < duration; t += 50) {  
        e.style.left = startPos.x + (endPos.x - startPos.x) * (t / duration);  
        e.style.top = startPos.y + (endPos.y - startPos.y) * (t / duration);  
        $await(Jscex.Async.sleep(50));  
    }  
    e.style.left = endPos.x;  
    e.style.top = endPos.y;  
}));  

Barrier模式

Barrier是一道篱笆,所有的不同异步线程,都先先后后运行完毕以后(都撞到了篱笆上),再汇成一束主流程继续往后走:

同步、异步转化和任务执行

JDK的CyclicBarrier类就是用来实现这个模式的(A synchronization aid that allows a set of threads to all wait for each other to reach a common barrier point.):

class Solver {  
    final int N;  
    final float[][] data;  
    final CyclicBarrier barrier;  
  
    class Worker implements Runnable {  
        int myRow;  
  
        Worker(int row) {  
            myRow = row;  
        }  
  
        public void run() {  
            while (!done()) {  
                processRow(myRow); //执行某一行的逻辑  
  
                try {  
                    barrier.await(); //该行逻辑执行完毕后,告知一下  
                } catch (InterruptedException ex) {  
                    return;  
                } catch (BrokenBarrierException ex) {  
                    return;  
                }  
            }  
        }  
    }  
  
    public Solver(float[][] matrix) {  
     data = matrix;  
     N = matrix.length;  
     barrier = new CyclicBarrier(N,  
                                 new Runnable() {  
                                   public void run() {  
                                     mergeRows(...); //在每一行都处理完毕以后,执行一个merge操作  
                                   }  
                                 });  
     for (int i = 0; i < N; ++i)  
       new Thread(new Worker(i)).start();  
  
     waitUntilDone();  
   }  
}  

而在JavaScript中,也可以实现类似的效果:

var count = 3;  
for(var i=0; i<=count; i++){  
    setTimeout(function(){  
        doXXX(); // 执行任务  
        count --; //每个子任务执行完毕后都标记一下  
        if(!count)  
            doFinalXXX(); //Barrier的汇总任务  
    },0);  
}  

如果有了Jscex,实现可以更简洁:

function (taskA, taskB, taskC) {  
    $await(Jscex.Async.parallel(taskA, taskB)); //先并行执行任务A、B  
    $await(taskC); //在A、B都完成后再执行C  
}  

Future和Promise

Future、Promise是用于并发编程的一种同步构造。它们表示一个对象,这个对象用来作为一次计算的结果的代理,而该结果被初始化为未知,因为这个对象产生时计算还没有结束(或还没有开始)。

Java中有Future可以帮助实现:

ExecutorService executor = Executors.newSingleThreadExecutor();  
Callable<Object> task = new Callable<Object>() {  
    public Object call() throws Exception {  
        doXXX();  
        return result;  
    }  
};  
Future<Object> future = executor.submit(task);  
boolean isCancelled = future.isCancelled(); //查询状态,调用cancel方法可以取消任务  
boolean isDone = future.isDone(); //查询状态  
Object res = future.get(); // 等待至完成  

JavaScript可以实现成类似这样子:

Promise.when(promise1, promise2).then(function (data1, data2) {...});  

具体请参见这里

文章未经特殊标明皆为本人原创,未经许可不得用于任何商业用途,转载请保持完整性并注明来源链接《四火的唠叨》

分享到:

2 comments

  1. [...] CyclicBarrier.class,也是计数等待,只不过它是利用await方法本身来实现计数器“+1”的操作,一旦计数器上显示的数字达到Barrier可以打破的界限,就会抛出BrokenBarrierException,线程就可以继续往下执行;请参见我写过的这篇文章《同步、异步转化和任务执行》中的Barrier模式 [...]

  2. [...] 同步、异步转化和任务执行 [...]

发表评论

电子邮件地址不会被公开。

您可以使用这些 HTML 标签和属性: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>


Preview on Feedage: