大家对进程和线程都很熟悉,但是对于纤程(或是协程)感到陌生。
其实协程在很早的时候就已经开始使用了。
最开始使用进程,发现对内存资源的消耗过大,于是采用了一个进程里面跑多个线程,这样减小了系统内存的压力(c10k问题),然而还是顶不住需求的不断增大。select基于事件驱动便应运而生,select是基于轮寻策略的事件处理方式,在select里注册事件后,select轮询所有的事件,把可读,可写,有异常的事件返回。这样大大的提高了效率。之后为了提高select的性能,epoll出现了(之间有poll增加了select1024个端口监测的限制)。
epoll采用了在内核中注册回调函数,当有读写请求的时候,回调函数会把请求对应的socket写在对应的链表里面。遍历链表就可以获取到整个io请求,最后会在红黑树上查找是否有注册的这个请求。
基于事件驱动效率很高,但是对于开发人员并不太友好(想想在回调函数中嵌套再嵌套的场景吧-,-哈哈)。这里借用知乎的一个例子
链接:https://www.zhihu.com/question/32218874/answer/56255707
假设你有10个文件,你想把10个文件拼接起来模拟成一个大文件。但是你内存又没那么大,那只好读一点让用户消耗一点了。所以你得写个filecat这样的adaptor。
如果所有接口都用同步,用户是爽了,仿佛直接在读一个文件;你实现得很蛋疼,每当用户问你要数据的时候,你得先检查我现在读到第几个文件了,读完了没有,后面还有没有别的文件了,要维护一坨状态。
如果所有接口都用异步,你是爽了,直接把用户的回调挨个传到不同的文件读写调用里去;用户则蛋疼了。要是用户想读三行,停一停去做些别的(比如处理下这三行),是很麻烦的,因为你这个adaptor往用户端塞数据塞得根本停不下来,用户有什么想法都得往回调函数里塞。“我现在读到第几行了?如果小于三行,就先送到处理头三行的函数里去;如果等于三行,就要拿一下处理好的三行并送去不知道什么地方,如果大于三行,再干点别的。。。”所以用户得手动维护这些状态。
你会发现无论是同步还是异步,都有这种“手动维护状态”的问题。要想让adaptor和用户都开心,解法自然是协程。我现在就用代码来表示状态,执行一行就是状态的转移。但是两头的状态要交替变化,这边做三行,轮到那边做了;那边做完了,又回到这边来,这跳来跳去的,就是协程了
然而纤程并不是银弹,是在损失了少量的性能的情况下做出的妥协。(话说红黑树不也是妥协的结果么-,-0)cpu负载增加,内存跑热,提升了io吞吐量。
网友评论