这个其实看vert.x会非常清楚
vert.x里面有两个线程池(缺省)如果你自己不再开线程池的话
一个是eventloop线程池,还有一个是worker thread pool线程池
协程(coroutine)和纤程(fiber)的主要区别点在于:调度
这两个单词都翻译得不错,coop协作,co就是协的词根,coroutine翻译成协程很巧妙
fiber是纤维,翻译成纤程,对应的thread/线程,process/进程,这几个单词翻译得都比较巧妙
说回coroutine和fiber
coroutine和fiber的差异点在于,两者的调度不一样,异步api在结果出来前,会释放当前线程,那么在此期间,当前线程就能去搞其他的计算活动,那一个最原始的异步api,一般是以带有callback的形式出现,但是callback有一个问题,就是写起来会变得混乱,因为callback要嵌套进参数里面去
如果多几层异步调用的话,就会形成缩进金字塔
那怎么办呢?一种解决方式就是通过声明continuation的scope,将scope里面的所有状态全部寄存,然后用continuation.yield释放当前线程,然后等结果出来之后,当前线程再从寄存器里取出状态,继续往前执行
那这里有几个问题,不一一列举,有兴趣的自己去搞懂这里面的机制,建议多看vert.x
有一个最重要的差别点就是,当这个结果出来之后,后续的代码,是由哪个线程在执行?
**如果还是原来线程在执行,那么就是coroutine**
**如果存在有被其他线程所执行的可能,那就是fiber**
这里可以看出来,fiber多了一个调度器,scheduler,所以一般fiber在启动的时候,会开一个carrier thread pool,线程池,大多数语言叫这个线程池叫做fork & join pool,既然是pool,那里面的线程不止一个,那fiber如果使用了异步操作,这里就**有可能**被一个以上的线程先后执行,注意这里是有可能,因为调度器有可能会把后续的步骤调度回原来的线程,但是这个完全由调度器决定
那这样做的好处是什么呢?客户在这里使用blocking code,因为这个流程基本上**完全模拟**了线程操作,线程在io时候是怎样被blocked,这里的纤程也会怎样被blocked
坏处是什么呢?嗯,跟线程一样,它需要一个完美的stack,线程就有自己的stack,那这个stack可能会比线程的stack要小,但是会比coroutine的stack要大很多,同时调度上也有一定程度的消耗,当然这个消耗可能不大,但还是有,因为可能被其他线程所执行,所以要在yield之前,把当前线程的所有状态全部寄存,否则resume的时候,状态丢了就完蛋了
那coroutine呢?coroutine则保证会使用原来的线程,所以会有eventloop的概念,node.js就用了eventloop,还有vert.x,那因为是原来的线程,那这里会有问题,一个是线程可能过度负荷,导致一个线程在忙,其他线程在观望的情况,所以这就出现了eventloop的一个坏处
那就是,你需要估算eventloop的执行时间,不能让它被blocked,这就对程序员提出了更高的心智要求
好处是什么呢?性能
因为都是同一个线程在执行,那么yield的时候,需要保存的状态就少了很多,kotlin里面一个coroutine的消耗才区区几百个字节,一般的fiber做不到这么低
同时因为调度的都是原来的线程,所以调度的损耗也减少了,所以总体而言,coroutine+eventloop的性能,要高过fiber+fork&join pool的性能,但是同时,对开发人员的心智要求,也相应提高了
说一下java的进度,java的loom正在如火如荼地开展中,vert.x已经实现了eventloop pool,但是目前java的语法,还只能实现future(monad)和callback两种api,还做不到await和scheduler,因为这两者的实现,都需要project loom的continuation yield/scope和scheduler这些,但是我们已经可以在kotlin中先行体验coroutine,其实也有fiber,用launch(common pool)加上缺省的调度器,就是fiber了
同时clojure也提供了core.async,也提供了fiber,缺省用的就是fork&join pool
所以还是要搞vert.x,否则这两者你没有办法完全体验到,像node,只有eventloop和await,没有fiber,像go就是强制使用goroutine(就是fiber),就没有coroutine,一般的java工具,还停留在thread阶段,只有vert.x这种多语言的工具,可以提前接触kotlin等语言,这些概念就都会出现
* * *
刚看到一个相关问题,应该说c++的boost是最早的fiber和coroutine的出处了
虽然不是java,但是java的概念也从中延伸而来,这个问题的高票答案也说到了
fiber带了一个调度器,coroutine则不带,java一样的
- 基础
- 简介
- 主要特征
- 变量和常量
- 编码转换
- 数组
- byte与rune
- big
- sort接口
- 和mysql类型对应
- 函数
- 闭包
- 工作区
- 复合类型
- 指针
- 切片
- map
- 结构体
- sync.Map
- 随机数
- 面向对象
- 匿名组合
- 方法
- 接口
- 权限
- 类型查询
- 异常处理
- error
- panic
- recover
- 自定义错误
- 字符串处理
- 正则表达式
- json
- 文件操作
- os
- 文件读写
- 目录
- bufio
- ioutil
- gob
- 栈帧的内存布局
- shell
- 时间处理
- time详情
- time使用
- new和make的区别
- container
- list
- heap
- ring
- 测试
- 单元测试
- Mock依赖
- delve
- 命令
- TestMain
- path和filepath包
- log日志
- 反射
- 详解
- plugin包
- 信号
- goto
- 协程
- 简介
- 创建
- 协程退出
- runtime
- channel
- select
- 死锁
- 互斥锁
- 读写锁
- 条件变量
- 嵌套
- 计算单个协程占用内存
- 执行规则
- 原子操作
- WaitGroup
- 定时器
- 对象池
- sync.once
- 网络编程
- 分层模型
- socket
- tcp
- udp
- 服务端
- 客户端
- 并发服务器
- Http
- 简介
- http服务器
- http客户端
- 爬虫
- 平滑重启
- context
- httptest
- 优雅中止
- web服务平滑重启
- beego
- 安装
- 路由器
- orm
- 单表增删改查
- 多级表
- orm使用
- 高级查询
- 关系查询
- SQL查询
- 元数据二次定义
- 控制器
- 参数解析
- 过滤器
- 数据输出
- 表单数据验证
- 错误处理
- 日志
- 模块
- cache
- task
- 调试模块
- config
- 部署
- 一些包
- gjson
- goredis
- collection
- sjson
- redigo
- aliyunoss
- 密码
- 对称加密
- 非对称加密
- 单向散列函数
- 消息认证
- 数字签名
- mysql优化
- 常见错误
- go run的错误
- 新手常见错误
- 中级错误
- 高级错误
- 常用工具
- 协程-泄露
- go env
- gometalinter代码检查
- go build
- go clean
- go test
- 包管理器
- go mod
- gopm
- go fmt
- pprof
- 提高编译
- go get
- 代理
- 其他的知识
- go内存对齐
- 细节总结
- nginx路由匹配
- 一些博客
- redis为什么快
- cpu高速缓存
- 常用命令
- Go 永久阻塞的方法
- 常用技巧
- 密码加密解密
- for 循环迭代变量
- 备注
- 垃圾回收
- 协程和纤程
- tar-gz
- 红包算法
- 解决golang.org/x 下载失败
- 逃逸分析
- docker
- 镜像
- 容器
- 数据卷
- 网络管理
- 网络模式
- dockerfile
- docker-composer
- 微服务
- protoBuf
- GRPC
- tls
- consul
- micro
- crontab
- shell调用
- gorhill/cronexpr
- raft
- go操作etcd
- mongodb