关于回调与协程

本文探讨了回调函数在异步编程中的优点和缺点,如通用性、灵活性和可定制性,以及回调地狱的问题。同时,作者介绍了腾讯的libco协程库和C++20的无栈协程,对比了它们与回调的差异,强调了协程在提高代码可读性和定制性方面的优势。

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

关于回调与协程

最近在看muduo网络库,再读源码时给了我一点小小的回调震撼,而异步回调和协程都改变了串行的执行顺序,实现了并发,由此引发了以下思考。

注:并发是短时间内多个任务都在向前推进(无论顺序,执行了多少)所以可以是单核。

​ 并行是指“同时”进行,也就是多核。

回调函数

在我看来,回调有通用、灵活、可定制的好处。

通用

通过给不同对象注册回调,可以减小不同对象间的耦合,给不同的功能任务分层,哪里出现问题,只需要在对应的对象里更改就好了,不对其他对象产生影响。

比如channel封装了fd,其最基础的的功能只有开启关闭监听事件类型,具体如何处理事件看给谁给它注册了callback。Acceptor就是accept;tcpconnect是read客户端传输的数据,读完再调用用户注册的回调;EventLoop是其他线程对当前线程的实时唤醒。

灵活

灵活性指的是不用把每一种可能的排列组合都写死在程序。read_event_for_Acceptor、read_event_for_EventLoop……以此类推,这样程序员就真成码农了。虽然模板元编程也能做到只编写一次代码,但它也是把实际的for_***的不同函数推导出来了,会造成代码膨胀。(而且思维没转过来之前模板元编程的可读性也很差)

可定制

可定制就是指在编写库时,可以暴露给用户注册回调的函数,来满足使用者的定制化功能。


说完好处,我就要开骂了。这玩意我算是初步理解什么叫回调地狱了,这我跟着别人整理好的带注释、精简版看,大体捋下来都花了一周。硬是看着他整理出的流程图、在全局ctrl+F找变量名、函数名,才看懂的。


协程

看了腾讯的libco协程库,其实相比说他是个协程库,他更像个非阻塞网络库。

它通过dlsym机制 hook 了各种网络 I/O 相关的系统调用,把诸如read()、write()和connect()等系统调用挂到epoll,把socket调用强制加上非阻塞,使得用户可以以“同步”的方式直接使用,把回调的被动使用,改为主动的“等待”(指被切走)。

其内部也是一个eventloop死循环,把监听到的活动事件和超时事件的回调加到active_list(还会有协程运行时cond_signal加入进来的事件)运行。相当于muduo抽象出的eventloop+channel。

超时事件: 是通过注册到时间轮,epoll每次返回推动时间获取的,以牺牲时间的精确度来提高效率。但我看代码没看懂,其中类的字段、继承关系,没注释,类名,变量名又缩略的太多,我真看不懂。在这贴一个能看懂的时间轮

通过它就不用再注册回调了,直接在Acceptor上accept,tcpconnect上read就行了,因为libco是非对称,有栈 协程,有"栈"(是广义上的栈数据结构,不是堆栈)存储协程创建运行顺序,就相当于把一个完整的线程栈切成了多个协程栈,代价是要切换上下文和栈空间,换来了以"同步"的思维方式编写代码,增加可读性。但在编写库的时候,还是需要用户注册回调函数,来实现定制化功能。(结构上也有可能是因为服务端是被动的所以需要回调)


关于c++20的协程是无栈协程,通过co_return co_yield和 co_await关键字来交互,用await切换上下文来代替有栈的调用关系,yield比await在切走前自动多执行一个函数。

但coroutine只是提供了上下文切换,调度结构的支持,还要用promise_type和返回类型中的await_ready、 await_suspend、 await_resume来自己制定调度规则,handle获得协程实例来获取中间状态,进行任意的调度。我要是想加到muduo里好像除了更改了回调的执行方式,还会加更多的工作量。(不过有人在写协程库co_context了,好像用起来挺方便的)

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值