一、问题的思考
以前我们在做多线程开发时,经常提到线程池的概念,那么为啥要有线程池的概念那?为啥在golang开发时,很少有人提到协程池的概念?
二、思路
线程的创建、销毁以及线程调度往往牵扯到资源内核态与用户态的切换,是比较消耗资源的,如果我们在进行多线程编程时,代码运行中动态创建线程资源,从高性能的考虑出发,必然会影响服务整体的性能。因此,线程池的存在很有必要。而golang的协程是运行在用户态的,因此协程的调度,不涉及内核态、用户态的切换,这里可以看下GMP模型深入理解下。线程相比协程占用更多的资源,比如一个线程大约占用8M的内存,而一个协程,则占用4K的内存,既然协程这么轻小,在我们开发时是否有必要做协程池设计思考?这里结合压测数据以及一些想法发散下。
三、golang协程池模型
3.1 模型1-动态创建协程消费task
每收到一个task,创建一个协程处理该task,压测结果如下:
从bench压测数据看,每次循环执行的时间基本上为66us.
3.2 模型2-固定worker消费task
具体模型可参考https://studygolang.com/articles/15477,刘丹冰分享的《Golang的协程池设计》,具体模型如下:
就以4个worker协程消费task为例(因为压测的服务器为mac pro,四核),我们压测下,数据如下:
从bench压测数据看,每次循环执行的时间基本上为55us.
相比第一个模型从压测数据看,每次循环节约了大约11us。
总结:
1、如果程序并发创建协程数据量很大,每个协程处理任务的事件较长,需要维持协程池,毕竟服务器的资源是有上限的,到达一定数量之后,会导致协程数过多而产生CPU负载较高的情况。
2、从自己压测数据看,创建协程的过程确实比较小的系统开销,通过上述压测数据来看也只有11us的差距。
讨论:
是否有更加高效的高性能协程池模型?
增加一个dispatcher模块,根据msg的ID做dispatcher分发到不同的协程channel中是否更加高效?用户可自己实现dispatcher以及message的处理方式,具体模型如下:
具体代码我已经提到到git上:https://github.com/go-snail/pool.git
压测数据如下: