java协程处理IO_协程与异步 IO(转载)

文章探讨了协程在实现异步编程中的作用,指出仅靠协程不足以实现异步效果,还需要底层IO的支持。文中提到了Python通过monkeypatch实现协程与IO的结合,而其他语言如Java、C++由于底层库不支持,协程应用受限。同时,文章指出了使用协程需要注意的事项,如避免同步IO、关注调度公平性和内存开销,并特别讨论了Go语言中goroutine的实现和调度优化。

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

这篇文章解释了我心中很多关于协程的疑问。

以下为正文。

协程(coroutine)的概念已经广为人知,这里就不多说了。作为用户态主动调度的执行单位,协程非常的轻量,并且调度是协作式的,协程可以避免传统多线程程序的上下文切换、调度和锁竞争等开销。

很多人都有这个误解,认为有了协程,就可以用同步程序的方式,写出异步的程序,原先同步的程序和第三方库,也会自动变成异步的。为什么说这是个误解呢,因为要写出有异步效果的程序,只有协程是不够的,还需要有底层IO的支持。协程的调度原本是编程者手工来做的,但是和异步IO结合的话,在发生IO时,自动将IO操作交给异步实现去执行,并让渡出协程的执行权,由调度器去调度执行其他协程。

除去天生支持协程或变种协程的lua, stackless-python, golang 之外,一个本不支持协程的语言也可以通过各种变通方法,来提供对协程的支持。事实上,基本上所有的的语言都有对协程支持的第三方实现,如 Java 的 Kilim, c++ 的boost.coroutine, Python 的 eventlet 和 gevent 等。

问题是,这些语言的底层IO实现,并未对协程调用做处理,其结果是仍然会阻塞这个协程,并没有实现异步的效果。而常用的http /数据库/缓存等lib 都是基于这些语言的底层IO库实现的(如httpclient/libcurl/mysql-connector等),所以使用了这些库的话,在IO 操作时不会让渡执行权,在当前协程阻塞在IO操作上的时候,其他协程也完全无法执行,这比多线程的实现还要糟糕。

除了 IO 之外,第三方lib 如果使用了线程,或是使用了锁,信号量等线程下的同步机制,或是使用了同步的Queue等,在协程环境下使用的时候也会出现各种问题。

所以,java, c++这样的语言,虽然有协程的实现,但是使用范围并不广,因为协程不能利用原先已经存在的大量lib ,需要按照协程的方式重新实现一遍。

一个例外是Python。 Python这样的动态语言,可以使用monkey patch的方式替换系统的IO / thread / queue / lock 等实现, 将其替换成对应的协程。Gevent就是这种做法。

而lua,stackless, golang 这样天生支持协程的语言,所有的IO lib 都是协程实现的,自然可以放心使用。

虽然非常简单,使用协程依然有需要注意的地方。比如:

协程不能有同步IO, 但并非所有的IO操作都一定是异步的。例如文件IO、Pipe、终端输入输出、DNS 解析等,经常没有异步的实现,通常的实现会用线程池进行一个封装,但依然需要在意。

协程只有在发生IO的时候才会让渡执行权,因此存在调度公平性的问题。在大量cpu操作、没有IO的情况下,当前协程会一直占用执行,其他协程得不到执行的机会。所以协程要注意不要有大量密集CPU操作。

协程的开销。协程依然需要占用一定的内存,如goroutine 目前是最小4k 的空间占用。协程切换的代价比较小,但不等于没有,比如stackfull 的协程实现在协程切换是需要做context copy,也有一定的开销。相比较纯异步API 编写的程序,协程的效率通常会差一些。

关于goroutine

goroutine的实现并不完全是传统意义上的协程。在协程阻塞的时候(cpu计算或者文件IO等),多个goroutine会变成多线程的方式执行。golang1.2之后还有类似erlang reduction的机制,来改善goroutine调度的公平性。这个机制只有在函数调用等场合下才会生效,所以效果还比较有限。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值