55

BRPC的精华都在bthread上啦(四):尾声

 3 years ago
source link: https://zhuanlan.zhihu.com/p/350582218
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

关于bthread的源码剖析洋洋洒洒在知乎也已经发表了三篇文章,在公众号号中将文章重组拆解后也已经发表了五篇。其实这类源码剖析类的文章从来都不好写。首先因为代码的冗杂,在解读的时候没办法找到一个”线性“视角,可以一以贯之的解读下来。比如讲A知识点的时候就会涉及到B,但展开讲B,整篇文章就会缺少主线,不去讲B,会可能造成讲解A到时候有些含混不清。又或者有一些brpc源码之外的基础知识,不确定读者是否都有足够的知识背景,所以是否要扩展解读也要费思量。

凡此种种,如何清晰地划分每一篇文章的边界,确定好每部分知识点的详略,确实让笔者费了不少功夫。如今bthread源码的主要解读都已经完毕,后续可能会涉及到一些细节补充。但主体确实已经完结,当然这并不是brpc源码解读系列的完结,因为brpc中不仅有bthread,还有很多其他可以值得讲解的东西。

如果你认真读完了笔者前面的那些文章,不知道你会不会产生一些疑问。可能我写的东西你都能看懂,但对于bthread还是有些疑问。没关系,质疑是学习的最好帮手,要善于提问,用于提问。下面我试着站在大家的角度,来提出一些FAQ,从而帮你更好地观察bthread理解上的任督二脉!

1. bthread_make_fcontext()虽然传入了栈大小,但是却没有被用到?

context = bthread_make_fcontext(storage.bottom, storage.stacksize, entry);

bthread_make_fcontext()最终会调用汇编函数make_stack(),其中用到了第一个参数(栈底),却没有用到栈大小。那么你是否有疑问,如果没有用到栈大小,那么是不是前面给bthread设置栈大小的属性参数就没有用呢?

非也非也。这里虽然汇编函数内确实没有用到栈的大小,但是storage.bottom其实已经是bthread可用的栈的最高地址了。回顾bottom的分配策略我们可知:

s->bottom = (char*)mem + memsize;

分配的栈大小确实是和bthread设置栈大小的属性参数有关的,然后这里返回的bottom是最高地址。也就是说时间在栈切换过去执行具体的协程任务的时候,由于栈是自高向低分配的,所以只要你使用的栈空间不超过大小就是OK的。在新局部变量分配的时候并没有进行边界检查,这在C语言里面亦是如是。当你使用的栈空间超过的时候,这里确实也会越界。

我曾经遇到过使用一个某个第三方库,会导致程序会core掉,归根结底是因为我设置的的栈大小是SMALL的,而这个库用到的栈空间比较多,改成NORMAL之后core解决。

2. 为什么要用汇编做任务切换,让worker(pthread)不停的从任务队列里取出任务去执行不就好了么?

看完前面的文章你可能会疑惑,Task已经封装到任务队列里了(rq,remote_rq),直接遍历队列取出任务,执行其中的回调函数就OK啦。干嘛要用汇编做一套make_stack和jump_stack呢?

我可以理解你的想法,在没有协程概念之前,很多线程池的涉及都如你这般,包括work stealing也可以被普通的线程池采纳。但是这里你永远要记住bthread的协程属性。所谓协程就是可以被中断的,也就是说你的bthread回调函数可以执行一半就退出worker,然后让给其他的bthread任务,等后续再唤醒回来继续执行刚才的运行了一半的任务。

这里的中断与否,其实就是看你的回调函数中是否有涉及到IO。比如brpc的Channel操作。如果你在通过RPC访问其他的brpc服务或者HTTP服务或者Redis等,这些操作都会让你的bthread退出,解除对worker的占用。待到下游response返回之后,才继续找到一个闲置的worker来处理你运行了一半的任务。这个中细节其实也颇为复杂,后续我会开篇去讲Channel的CallMethod与bthread的关联。

3. bthread适合做什么与不适合做什么

通过bthread,我们可以很方便的实现一个并行的调度库,我在工作中确实有见到过基于bthread实现的并行调度库,比如实现一个DAG在线调度引擎。借助于强大的bthread,我们只需要对于bthread_start_background()调用和封装一下就能实现。最复杂的部分brpc已经帮我们做了。

但诚然如此,我们还是要知道,bthread主要还是在充分利用的网络IO时的计算资源,通过M:N的协程来实现CPU利用的最大化。如果你的程序不是IO密集型的,只是单纯的计算密集型的,你当然也可以通过bthread来做并行化(比如for循环顺序处理无10000个元素,可以做出并行化的),但你需要知道这种场景使用bthread并不会比那些老牌的并行计算库更有优势,比如OpenMP、TBB等。

bthread支持类似omp的并行计算的方法 · Issue #965 · apache/incubator-brpc

4. 其他问题

其他问题欢迎提问

欢迎关注我的公众号:编程往事

http:// weixin.qq.com/r/EXUyKm7 E3TOirSpJ9yAD (二维码自动识别)


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK