java协程处理IO_协程,高并发IO的终极杀器(2)

本来打算在第二节课讲 async / await 语法的,写着写着发现,async / await 语法虽然从语言实现者的角度看,难度比原生的coroutine要简单,但是对于用户而言,其实非常不好理解,反倒是功能最强大,语法最清晰的完整的coroutine是最容易理解的。所以这节课,我直接跳到Go语言,来看一下,一个真正的stackful的coroutine是怎么样的。

从我个人而言,十分推崇Go语言,但对于Go的前景却又不十分看好,产生这么矛盾的看法的一切根源都来自于Go语言所实现的协程。一方面,协程在分布式场景中一定会发挥巨大的作用,另一方面,Go语言的协程并不什么特别了不起的核心技术,在C++或者C中实现一个协程并不是什么特别难的挑战,稍有一些操作系统,汇编经验的程序员就可以完成,例如,我已经见过,用过,实现的C++版本的协程不下3个了。所以Go除了要把协程这一个特色做好之外,在GC,编译效率和编译效果上面还要下大功夫才行。

未来的服务端架构往微服务的方向演化是一种必然。微服务之间的通讯,同步模型性能不行,异步模型太难写。协程是最好的方案。这个时候,把协程做为语言的主要卖点,在runtime完整地实现了协程的Go语言的兴起就是情理之中的事情了。

好了,我们先看一下Go的例子吧:

package main

import (

"fmt"

"time"

"runtime"

)

func sleep(s string) {

var i int = 0

for {

i += 1

fmt.Println(i)

fmt.Println(s)

time.Sleep(time.Second)

}

}

func main() {

fmt.Println(runtime.GOMAXPROCS(0))

go sleep("Hello")

go sleep("World")

time.Sleep(1000 * time.Second)

}

运行一下这个例子:

可以看到,Hello 和 World交替打印出来,这就像是两个线程在并行执行一样。实际上,在Go语言里,go关键字的作用就是开启一个新的协程,再来看这两句:

go sleep("Hello")

go sleep("World")

这两句开启了两个协程,每一个协程所要做的事就是打印传进来的参数。打印完以后进入sleep。go里的time.sleep并不会使线程休眠,而是使协程休眠。其实这两个协程是运行在同一个线程上的,通过这行语句:

fmt.Println(runtime.GOMAXPROCS(0))

这行语句可以看出来当前协程所运行的线程的个数是1。

它们之间是这样的一种关系:

A,B ,C,D四个协程,都属于同一个线程。当A进入sleep的时候,实际上,就是把A的上下文环境保存起来,然后这个线程转到B上去执行(当然,这个转过去,本质上就是切换一下RSP寄存器就好了,具体可以类比线程间的切换,GO语言的实现是每个协程都有自己的独立栈空间,所以切换时要做的事情很少)。当B再进入sleep时,就会再转向C去执行,依次类推。

当线程再切回A的时候,它要把A的上下文再恢复,对于我们的例子,统计次数用的变量 i 就位于这个上下文中,当我们从其他协程切回到A的时候,上一次如果i是3的话,那么这一次就会变成4。所以协程最重要的功能就是在休眠的时候线程不休眠,保存上下文,让线程去执行其他的协程。

为什么协程能处理高并发

由于Go语言已经很好地封装了IO接口,这使得我们无法一窥全貌,明天我会使用一种Java上比较流行的协程库来讲解一下,为什么协程可以处理高并发IO。

今天最重要的是理解协程的概念,至于它的优点,原理,调度等等,我会在后面的课程继续讲解。

作业:

安装一个Go,让一份简易的教程,体验一下Go语言。例如:Go 语言变量 | 菜鸟教程

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值