文档章节

对协程的一些理解

ksfzhaohui
 ksfzhaohui
发布于 2016/12/09 18:11
字数 1542
阅读 4725
收藏 110
点赞 5
评论 16

协程
协程(coroutine)最早由Melvin Conway在1963年提出并实现,一句话定义:协程是用户态的轻量级的线程

线程和协程
线程和协程经常被放在一起比较;线程一旦被创建出来,编写者是无法决定什么时候获得或者放出时间片的,是由操作系统进行统一调度的;而协程对编写者来说是可以控制切换的时机,并且切换代价比线程小,因为不需要进行内核态的切换。
协程避免了无意义的调度,由此可以提高性能,但也因此,程序员必须自己承担调度的责任,同时,协程也失去了标准线程使用多CPU的能力, 但是可用通过多个(进程+多协程)模式来充分利用多CPU。
协程另外一个重要的特点就是:协程是作用在用户态,操作系统内核对于协程是毫无感知的,这样一来,协程的创建就类似于普通对象的创建,非常轻量级,从而使你可以在一个线程里面轻松创建数十万个协程,就像数十万次函数调用一样。可以想象一下,如果是在一个进程里面创建数十万个线程,结果该是怎样可怕。

进程、线程、协程
进程:拥有自己独立的堆和栈,既不共享堆,亦不共享栈,进程由操作系统调度。
线程:拥有自己独立的栈和共享的堆,共享堆,不共享栈,线程亦由操作系统调度(标准线程是的)。
协程:和线程一样共享堆,不共享栈,协程由程序员在协程的代码里显示调度。

对协程的支持
Lua, Go,C#等语言原生支持协程
Java依赖第三方库,例如最为著名的协程开源库Kilim
C标准库里的函数setjmp和longjmp可以用来实现一种协程。

下面以lua语言为例子了解一下协程的工作机制

function foo(a)
    print("foo", a)
    return coroutine.yield(2 * a)
end

co = coroutine.create(function ( a, b )
    print("co-body_01", a, b)
    local r = foo(a + 1)
    print("co-body_02", r)
    return "end"
end)

print("---main---", coroutine.resume(co, 1, 10))
print("---main---", coroutine.resume(co, "r7"))

运行结果:

D:\>luac text.lua
D:\>lua luac.out
co-body_01      1       10
foo     2
---main---      true    4
co-body_02      r7
---main---      true    end

主要利用resume和yield两个函数进行控制切换的时机,具体描述看如下图(来源网上):

协程经常被用在遇到io阻塞操作的时候,直接yield让出cpu,让下面的程序可以继续执行,等到操作完成了再重新resume恢复到上一次yield的地方;有没有觉得这种模式和我们碰到过的异步回调模式有点类似,下面可以进行一个对比。

协程和callback
协程经常被用来和callback进行比较,因为都实现了异步通信的功能;下面以一个具体的场景来描述2者的区别:
A首先要走到B的面前,到B的面前之后,A对B说“Hello”,A说完之后B对A说“Hello”,注意这里的每个动作之前都有一段的延迟时间
这个场景如果用callback的方式来描述的话,会是这样:

A.walkto(function (  )
    A.say(function (  )
        B.say("Hello")
    end,"Hello")
end, B)

这边只用到2层嵌套,如果再多几层的话,真是非人类代码了,如果用协程来实现:

co = coroutine.create(function (  )
    local current = coroutine.running
    A.walto(function (  )
        coroutine.resume(current)
    end, B)
    coroutine.yield()
    A.say(function (  )
        coroutine.resume(current)
    end, "hello")
    coroutine.yield()
    B.say("hello")
end)

coroutine.resume(co)

结构清晰了不少,协程让编程者以同步的方式写成了异步大代码;
来源网上的一句总结:让原来要使用异步+回调方式写的非人类代码,可以用看似同步的方式写出来

不管是协程还是callback,本质上其实提供的是一种异步无阻塞的编程模式,下面看看java在这种模式下的尝试:

java异步无阻塞的编程模式
java语言本身没有提供协程的支持,但是一些第三方库提供了支持,比如JVM上早期有kilim以及现在比较成熟的Quasar。但是这里没打算就kilim和quasar框架进行介绍;这里要介绍的是java5中的Future类和java8中的CompletableFuture类。

1.Future使用

ExecutorService es = Executors.newFixedThreadPool(10);
Future<Integer> f = es.submit(() ->{
             // 长时间的异步计算
             // ……
             // 然后返回结果
             return 100;
});
//while(!f.isDone())
f.get();

虽然Future以及相关使用方法提供了异步执行任务的能力,但是对于结果的获取却是很不方便,只能通过阻塞或者轮询的方式得到任务的结果。
这种模式暂且叫它伪异步。其实我们想要的是类似Netty中这种模式:

ChannelFuture future = bootstrap.connect(new InetSocketAddress(host, port));
future.addListener(new ChannelFutureListener()
{
          @Override
           public void operationComplete(ChannelFuture future) throws Exception
           {
                if (future.isSuccess()) {
                      // SUCCESS
                 }
                 else {
                      // FAILURE
                 }
            }
});

操作完成时自动调用回调方法,终于在java8中推出了CompletableFuture类

2.CompletableFuture使用
CompletableFuture提供了非常强大的Future的扩展功能,可以帮助我们简化异步编程的复杂性,提供了函数式编程的能力,可以通过回调的方式处理计算结果,并且提供了转换和组合CompletableFuture的方法。这里不想介绍更多CompletableFuture的东西,想了解更多CompletableFuture介绍,看一个比较常见的使用场景:

CompletableFuture<Integer> future = CompletableFuture.supplyAsync(耗时函数);
Future<Integer> f = future.whenComplete((v, e) -> {
        System.out.println(v);
        System.out.println(e);
});
System.out.println("other...");

CompletableFuture真正的实现了异步的编程模式

总结
为什么协程在Java里一直那么小众,Java里基本上所有的库都是同步阻塞的,很少见到异步无阻塞的。而且得益于J2EE,以及Java上的三大框架(SSH)洗脑,大部分Java程序员都已经习惯了基于线程,线性的完成一个业务逻辑,很难让他们接受一种将逻辑割裂的异步编程模型。

个人博客:codingo.xyz

© 著作权归作者所有

共有 人打赏支持
ksfzhaohui

ksfzhaohui

粉丝 296
博文 126
码字总数 153828
作品 3
南京
高级程序员
加载中

评论(16)

陈清冈
陈清冈
用线程不是也可以完成异步无阻塞吗~
ksfzhaohui
ksfzhaohui

引用来自“pseudo”的评论

你可以看看rxjava
好的,谢啦
pseudo
pseudo
你可以看看rxjava
行者无疆0511
原来这个叫协程 学到了
loyal
loyal
扯淡,java是有无阻塞的,问题是早期的代码量太大.如果大家都用jdk8,以及使用基于jdk8的很多先进的库,都直接支持非阻塞的方式.但很多历史原因不那么容易升级到jdk8而已.
ksfzhaohui
ksfzhaohui

引用来自“BoXuan”的评论

node.js 6版本也支持协程了,yield的确比使用大量的闭包清晰很多,不过node.js也是单进程对应单线程的,cpu利用率低,不过有其它方式弥补,在一定条件下,逻辑不算太复杂的情况下,node.js的并发效果甚至比多线程的java要好,正如redis一样,它也是单线程的,它的高效只能在存取非常快的情况下,一旦存取很大的数据并发量就影响全局,拖慢整个系统。我们也不能忽视单线程机制的弊端。为什么java的协程库没有得到广泛应用,应该也跟java的设计初衷相违背,协程并不是万能的,但线程依赖操作系统,依靠CPU内核切换基本能满足所有需求,正因为有一层CPU内核切换在某种情况下肯定要弱于单线程的协程机制。
恩,其实我没打算去评论协程和线程哪个好,只是说协程的这种异步无阻塞的编程模式在java中也可以很好的体现
ksfzhaohui
ksfzhaohui

引用来自“BoXuan”的评论

我理解的协程应该是类似一个定时器线程中处理很多个定时任务,只是协程有另一套完整的流程。其实协程的缺点也很明显,执行复杂函数返回复杂的结果,异步执行但要使用yield保证类似同步代码的方式,必然有个中间堆栈保存结果。而且执行很多个复杂的协程的底层实现应该还是同步顺序执行的,要并行还得使用线程。
是的,需要保存上下文,这点和线程有点类似,但不能做到真正的并行
节节草
节节草
node.js 6版本也支持协程了,yield的确比使用大量的闭包清晰很多,不过node.js也是单进程对应单线程的,cpu利用率低,不过有其它方式弥补,在一定条件下,逻辑不算太复杂的情况下,node.js的并发效果甚至比多线程的java要好,正如redis一样,它也是单线程的,它的高效只能在存取非常快的情况下,一旦存取很大的数据并发量就影响全局,拖慢整个系统。我们也不能忽视单线程机制的弊端。为什么java的协程库没有得到广泛应用,应该也跟java的设计初衷相违背,协程并不是万能的,但线程依赖操作系统,依靠CPU内核切换基本能满足所有需求,正因为有一层CPU内核切换在某种情况下肯定要弱于单线程的协程机制。
节节草
节节草
我理解的协程应该是类似一个定时器线程中处理很多个定时任务,只是协程有另一套完整的流程。其实协程的缺点也很明显,执行复杂函数返回复杂的结果,异步执行但要使用yield保证类似同步代码的方式,必然有个中间堆栈保存结果。而且执行很多个复杂的协程的底层实现应该还是同步顺序执行的,要并行还得使用线程。
节节草
节节草
我理解的协程应该是类似一个定时器线程中处理很多个定时任务,只是协程有另一套完整的流程。其实协程的缺点也很明显,执行复杂函数返回复杂的结果,异步执行但要使用yield保证类似同步代码的方式,必然有个中间堆栈保存结果。而且执行很多个复杂的协程的底层实现应该还是同步顺序执行的,要并行还得使用线程。
nginx和lua的协程

1、缘起 我最早接触协程时,对这东西很费解,甚至我看了lua的协程源码实现后,这个疑虑还是没有消除。直到有次在需要用异步编程时,对协程的理解一下子明白了。所以希望用从异步的角度解释协...

那一剑的风情 ⋅ 2015/06/09 ⋅ 5

php-msf 源码解读

date: 2017-12-22 11:29:15 title: php-msf 源码解读 php-msf: https://github.com/pinguo/php-msf 百度脑图 - php-msf 源码解读: http://naotu.baidu.com/file/cc7b5a49dfed46001d22222b1af......

daydaygo ⋅ 2017/12/24 ⋅ 0

Kotlin从入门到放弃(四)——协程下

上一篇的kotlin讲到了协程的启动、等待和取消,这一篇对kotlin协程部分内容的补充。 挂起函数    接触到协程之后出现的一个新型的函数,以特殊修饰符suspend修饰的函数被称为挂起函数。挂起...

jara0705 ⋅ 02/12 ⋅ 0

swoole| swoole 协程初体验

date: 2018-5-30 14:31:38 title: swoole| swoole 协程初体验 description: 通过协程的执行初窥 swoole 中协程的调度; 理解协程为什么快; swoole 协程和 go 协程对比 折腾 swoole 协程有一段...

daydaygo ⋅ 06/14 ⋅ 0

异步I/O——协程

什么是协程? 协程,又称微线程,纤程。英文名Coroutine。协程的概念有点像子程序。 子程序,或者称为函数,在所有语言中都是层级调用,比如A调用B,B在执行过程中又调用了C,C执行完毕返回,...

爱秋刀鱼的猫 ⋅ 2017/12/13 ⋅ 0

深入理解Python中的ThreadLocal变量(下)

在上篇我们看到了 ThreadLocal 变量的简单使用,中篇对python中 ThreadLocal 的实现进行了分析,但故事还没有结束。本篇我们一起来看下Werkzeug中ThreadLocal的设计。 Werkzeug 作为一个 WS...

selfboot ⋅ 2016/11/03 ⋅ 0

一起学Python:协程-yield

协程-yield 协程,又称微线程,纤程。英文名Coroutine。 协程是啥 协程是python个中另外一种实现多任务的方式,只不过比线程更小占用更小执行单元(理解为需要的资源)。 为啥说它是一个执行...

祈澈姑娘 ⋅ 01/10 ⋅ 0

python-复盘-从yield/send到yield from再到async/await

Python中的协程大概经历了如下三个阶段: 最初的生成器变形yield/send 引入@asyncio.coroutine和yield from 在最近的Python3.5版本中引入async/await关键字 一、生成器变形yield/send 这个协...

时间之友 ⋅ 2017/11/24 ⋅ 0

PHP协程入门详解

概念 咱们知道多进程和多线程是实现并发的有效方式。但多进程的上下文切换资源开销太大;多线程开销相比要小很多,也是现在主流的做法,但其的控制权在内核,从而使用户(程序员)失去了对代码...

oneHand ⋅ 01/15 ⋅ 0

tornado的异步

tornado中的协程是如何工作的 本文将按以下结构进行组织,说明tornado中协程的执行原理 协程定义 生成器和yield语义 Future对象 ioloop对象 函数装饰器coroutine 总结 协程定义 Coroutines a...

io123 ⋅ 2016/07/24 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

Spring | IOC AOP 注解 简单使用

写在前面的话 很久没更新笔记了,有人会抱怨:小冯啊,你是不是在偷懒啊,没有学习了。老哥,真的冤枉:我觉得我自己很菜,还在努力学习呢,正在学习Vue.js做管理系统呢。即便这样,我还是不...

Wenyi_Feng ⋅ 今天 ⋅ 0

博客迁移到 https://www.jianshu.com/u/aa501451a235

博客迁移到 https://www.jianshu.com/u/aa501451a235 本博客不再更新

为为02 ⋅ 今天 ⋅ 0

win10怎么彻底关闭自动更新

win10自带的更新每天都很多,每一次下载都要占用大量网络,而且安装要等得时间也蛮久的。 工具/原料 Win10 方法/步骤 单击左下角开始菜单点击设置图标进入设置界面 在设置窗口中输入“服务”...

阿K1225 ⋅ 今天 ⋅ 0

Elasticsearch 6.3.0 SQL功能使用案例分享

The best elasticsearch highlevel java rest api-----bboss Elasticsearch 6.3.0 官方新推出的SQL检索插件非常不错,本文一个实际案例来介绍其使用方法。 1.代码中的sql检索 @Testpu...

bboss ⋅ 今天 ⋅ 0

informix数据库在linux中的安装以及用java/c/c++访问

一、安装前准备 安装JDK(略) 到IBM官网上下载informix软件:iif.12.10.FC9DE.linux-x86_64.tar放在某个大家都可以访问的目录比如:/mypkg,并解压到该目录下。 我也放到了百度云和天翼云上...

wangxuwei ⋅ 今天 ⋅ 0

PHP语言系统ZBLOG或许无法重现月光博客的闪耀历史[图]

最近在写博客,希望通过自己努力打造一个优秀的教育类主题博客,名动江湖,但是问题来了,现在写博客还有前途吗?面对强大的自媒体站点围剿,还有信心和可能型吗? 至于程序部分,我选择了P...

原创小博客 ⋅ 今天 ⋅ 0

IntelliJ IDEA 2018.1新特性

工欲善其事必先利其器,如果有一款IDE可以让你更高效地专注于开发以及源码阅读,为什么不试一试? 本文转载自:netty技术内幕 3月27日,jetbrains正式发布期待已久的IntelliJ IDEA 2018.1,再...

Romane ⋅ 今天 ⋅ 0

浅谈设计模式之工厂模式

工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。 在工厂模式中,我们在创建对象时不会对客户端暴露创建逻...

佛系程序猿灬 ⋅ 今天 ⋅ 0

Dockerfile基础命令总结

FROM 指定使用的基础base image FROM scratch # 制作base image ,不使用任何基础imageFROM centos # 使用base imageFROM ubuntu:14.04 尽量使用官方的base image,为了安全 LABEL 描述作...

ExtreU ⋅ 昨天 ⋅ 0

存储,对比私有云和公有云的不同

导读 说起公共存储,很难不与后网络公司时代的选择性外包联系起来,但尽管如此,它还是具备着简单和固有的可用性。公共存储的名字听起来也缺乏专有性,很像是把东西直接堆放在那里而不会得到...

问题终结者 ⋅ 昨天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部