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

本文深入介绍了协程的概念,作为一种轻量级的执行单元,协程占用资源少,调度发生在用户态。通过对比线程和协程,阐述了协程在并发编程中的优势,如避免回调地狱,简化代码维护。同时,通过Python和JavaScript的Generator示例,展示了协程如何工作。文章最后预告了下节课将探讨Kotlin和C#的协程实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

我用了这么多课程,讲了那么多NIO的相关的知识,终于来到了协程这一节课。

先声明一点,本节课看起来觉得看不懂的同学,不要着急,可以先放下,等慢慢理解了进程,线程的概念以后,再回来看这节课。

Callback Hell

Callback模型看起来,已经很不错了,但是,如果要连续地读取多个数值,那么多层的Callback就会嵌套在一起。这个嵌套会使得代码很难看,也很难维护。代码最好是能像客户端那样顺序地写。所有IO模型中,阻塞式的,顺序式的,同步代码是最直观,最简单的。

那我们能不能把这个callback修改成同步的代码呢?

答案是,能,这个方法就是协程。

协程的定义协程是个什么东西呢?它是一种轻量级的,用户态的执行单元。它占用的内存非常少,几乎是需要多少才用多少。相比起线程在创建之初就指定栈空间,协程所使用的内存可以动态地变化。它主要有两个特点:

占用的资源更少。

所有的切换和调度都发生在用户态。

协程拥有自己的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈。因此:协程能保留上一次调用时的状态(即所有局部状态的一个特定组合),每次过程重入时,就相当于进入上一次调用的状态,换种说法:进入上一次离开时所处逻辑流的位置。在并发编程中,协程与线程类似,每个协程表示一个执行单元,有自己的本地数据,与其它协程共享全局数据和其它资源。目前主流语言基本上都选择了多线程作为并发设施,与线程相关的概念是抢占式多任务(Preemptive multitasking),而与协程相关的是协作式多任务。

不管是进程还是线程,每次阻塞、切换都需要陷入系统调用(system call),先让CPU跑操作系统的调度程序,然后再由调度程序决定该跑哪一个进程(线程)。而且由于抢占式调度执行顺序无法确定的特点,使用线程时需要非常小心地处理同步问题,而协程完全不存在这个问题(事件驱动和异步程序也有同样的优点)。

协作式的任务,是要用户自己来负责任务的让出的。如果一个任务不主动让出,其他任务就不会得到调度。这是协程的一个弱点,但是好好的规划,这其实是一个可以变得很强大的优点。

关于协程,有一篇很好的文章:C/C++协程库libco:微信怎样漂亮地完成异步化改造

这是微信服务端的协程化改造,这里面也有很多关于协程的介绍。大家可以好好读一读。

其他语言中的协程

说了这么多,可能大家还是没有一个直观的感受。我还是举例来说明,从最简单的到复杂的,一点点来。

很多编程语言中都有协程。Lua, Ruby 等等都有自己的协程实现。Go完全就是因为协程而发展壮大的。协程的实现有很多方式,我可以从最简单的stackless的协程开始一点点为大家示例。

最简单的例子,我先用python举个例子:

def fib():

a = 0

b = 1

while True:

yield b

a, b = b, a + b

o = fib()

for i in xrange(10):

print o.next()

Python中使用的是一种名为Generator的东西。大家可以把这些代码保存到一个名为test.py中,然后使用python来执行它:

可以看到,这个程序可以打印出Fibonacci的前10项,实际上,只要你愿意,这个o.next()方法是可以一直执行下去的,可以打印出Fibonacci的任意项数。这个程序的奥秘就在于yield方法。

在Python中,如果函数定义中使用了yield语句,这个函数就会变成一个叫做Generator的东西,调用穿上函数时,返回的那个对象就是Generator,例如我们例子中的o。在Generator上调用next方法,就会开始真正执行fib中的逻辑。分别是把a设为0,b设为1,然后进入一个无限循环。在循环体中,遇到yield,函数调用就会退出,并且把 b 做为返回值返回到 next 的调用者那里。但是这个返回并不是真的就把函数全部退掉了,而是保存函数现场。这一步非常重要,这保证了下一次再调next 的时候,a, b 的值会是上次中断时a 和 b的值。

a, b 的值都是保存在 o 中的,o 就像一个可以源源不断地产生数字的发生器一样,所以给它一个名字叫Generator

理解了这个函数,大家对于协程保存现场可能会有一些初步的印象了。由于yield这种东西,只能保存一层函数调用,所以这种东西也叫 stackless coroutine。就是说整个函数调用栈,这里是无法保存的。

如果你的机器上没有python,那也不用去装,因为你还可以使用chrome来运行javascript的例子。打开chrome浏览器,点F12,然后选择console:

像我这样输入 fib 函数的定义,然后使用Generator来生成fibonacci数列。JS中的生成器与Python是一样的。

好了,今天就先讲这么多,下节课我们讲 Kotlin 和 C# 的实现,大家就能更清楚了。

上一节课:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值