Skip to content

四火的唠叨

一个纯正程序员的啰嗦

Menu
  • 所有文章
  • About Me
  • 关于四火
  • 旅行映像
  • 独立游戏
  • 资源链接
Menu

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

Posted on 10/24/201106/23/2019 by 四火

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

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

同步的好处:

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

image

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) {...});  

具体请参见这里。

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

×Scan to share with WeChat

你可能也喜欢看:

  1. 从 JavaScript 的单线程执行说起
  2. 几道容易出错的 JavaScript 题目
  3. A page widgetization practice
  4. 跨域方法汇总
  5. 笔记:线程的同步和互斥

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

订阅·联系

四火,啰嗦的程序员一枚,现居西雅图

Amazon Google Groovy Hadoop Haskell Java JavaScript LeetCode Oracle Spark 互联网 亚马逊 前端 华为 历史 同步 团队 图解笔记 基础设施 工作 工作流 工具 工程师 应用系统 异步 微博 思考 技术 数据库 曼联 测试 生活 眼界 程序员 管理 系统设计 缓存 编程范型 美股 英语 西雅图 设计 问题 面向对象 面试

分类

  • Algorithm and Data Structure (30)
  • Concurrency and Asynchronization (6)
  • System Architecture and Design (43)
  • Distributed System (18)
  • Tools Frameworks and Libs (13)
  • Storage and Data Access (8)
  • Front-end Development (33)
  • Programming Languages and Paradigms (55)
  • Testing and Quality Assurance (4)
  • Network and Communication (6)
  • Authentication and Authorization (6)
  • Automation and Operation Excellence (13)
  • Machine Learning and Artificial Intelligence (6)
  • Product Design (7)
  • Hiring and Interviews (14)
  • Project and Team Management (14)
  • Engineering Culture (17)
  • Critical Thinking (25)
  • Career Growth (57)
  • Life Experience and Thoughts (45)

推荐文章

  • 聊一聊分布式系统中的时间
  • 谈谈分布式锁
  • 常见分布式系统设计图解(汇总)
  • 系统设计中的快速估算技巧
  • 从链表存在环的问题说起
  • 技术面试中,什么样的问题才是好问题?
  • 从物理时钟到逻辑时钟
  • 近期面试观摩的一些思考
  • RSA 背后的算法
  • 谈谈 Ops(汇总 + 最终篇):工具和实践
  • 不要让业务牵着鼻子走
  • 倔强的程序员
  • 谈谈微信的信息流
  • 评审的艺术——谈谈现实中的代码评审
  • Blog 安全问题小记
  • 求第 K 个数的问题
  • 一些前端框架的比较(下)——Ember.js 和 React
  • 一些前端框架的比较(上)——GWT、AngularJS 和 Backbone.js
  • 工作流系统的设计
  • Spark 的性能调优
  • “残酷” 的事实
  • 七年工作,几个故事
  • 从 Java 和 JavaScript 来学习 Haskell 和 Groovy(汇总)
  • 一道随机数题目的求解
  • 层次
  • Dynamo 的实现技术和去中心化
  • 也谈谈全栈工程师
  • 多重继承的演变
  • 编程范型:工具的选择
  • GWT 初体验
  • java.util.concurrent 并发包诸类概览
  • 从 DCL 的对象安全发布谈起
  • 不同团队的困惑
  • 不适合 Hadoop 解决的问题
  • 留心那些潜在的系统设计问题
  • 再谈大楼扔鸡蛋的问题
  • 几种华丽无比的开发方式
  • 我眼中的工程师文化
  • 观点的碰撞
  • 谈谈盗版软件问题
  • 对几个软件开发传统观点的质疑和反驳
  • MVC 框架的映射和解耦
  • 编程的未来
  • DAO 的演进
  • 致那些自嘲码农的苦逼程序员
  • Java 多线程发展简史
  • 珍爱生命,远离微博
  • 网站性能优化的三重境界
  • OSCache 框架源码解析
  • “ 你不适合做程序员”
  • 画圆画方的故事

近期评论

  • Ticket: TRANSACTION 1.922915 BTC. Go to withdrawal >> https://yandex.com/poll/enter/BXidu5Ewa8hnAFoFznqSi9?hs=20bd550f65c6e03103876b28cabc4da6& on 倔强的程序员
  • panshenlian.com on 初涉 ML Workflow 系统:Kubeflow Pipelines、Flyte 和 Metaflow
  • panzhixiang on 关于近期求职的近况和思考
  • Anonymous on 闲聊投资:亲自体验和护城河
  • 四火 on 关于近期求职的近况和思考
  • YC on 关于近期求职的近况和思考
  • mafulong on 常见分布式基础设施系统设计图解(四):分布式工作流系统
  • 四火 on 常见分布式基础设施系统设计图解(八):分布式键值存储系统
  • Anonymous on 我裸辞了
  • https://umlcn.com on 资源链接
© 2025 四火的唠叨 | Powered by Minimalist Blog WordPress Theme