使用libstpool库实现线程池

最近在做一个通话相关服务器的项目,考虑到一个问题,就是每路通话需要新建一个线程,来处理话语权申请的过程。但是每路通话建立时需要新建线程,挂断时需要取消线程,这样势必对整个系统资源是一种很大的浪费。考虑再三,决定给通信服务器配两种模式,一种不断创建且销毁线程的模式,一种线程池模式。

java实现线程池是一个很简单的事情,但是C++就不一样了。在网上查找了很久的资料,发现了一个库听说挺好用的,就是libstpool,但是这方面的网上的资料也非常少,几乎都是从库中的example文件夹的示例程序拷贝了一下就完事,并且实际发现,网上的程序并不好用,几乎都是基于比较老的版本的example(实际测试编译都无法通过)。幸好这个库比较轻量级,看源代码不是特别的费劲,就直接看源代码得了。

首先说一说这个库的编译问题,在./configure时一定要指定安装路径,否则之后make install的时候,不会做任何事情。还有就是这个库的make在我的虚拟机中没有完全make通过,主要还是example中的文件编译有问题,但这个无伤大雅,make后直接忽略了编译错误,make install就行了。ps:我的libstpool是在github中下载的。

然后就是库的使用问题了,在stpool.h中有相应接口的简单说明,一些不常用的接口我就不说了,我就说说我在项目中实际用到的接口吧。

首先是stpool_create(“mypool”, eCAPs, 50,10,0,1)这个接口,这个接口实现了创建一个线程池的功能,第一个参数随意指定,第二个参数表示了所创建的线程池的一个属性,具体属性在stpool_caps.h中有具体的定义,我在项目中使用的参数为:

eCAP_F_DYNAMIC|eCAP_F_SUSPEND|eCAP_F_THROTTLE|eCAP_F_ROUTINE|eCAP_F_DISABLEQ|eCAP_F_PRIORITY|eCAP_F_WAIT_ALL|

eCAP_F_REMOVE_BYPOOL|eCAP_F_CUSTOM_TASK(可以参考一下)

第三个参数的意思就是创建的线程池中最多可以有多少个线程,第四个参数表示创建的线程池中至少有多少个线程。第四个参数和第五个参数不多说了,可以参考一下stpool.h中的说明。

接下来是int stpool_add_routine(stpool_t *pool, const char *name, void (*task_run)(struct sttask *), void (*task_err_handler)(struct sttask *, long reasons),void *task_arg, struct schattr *attr);这个接口。这个接口就是往线程池里扔任务了。第一个参数表示要使用哪个线程池;第二个参数是一个标识;第三个参数是一个回调函数,表示这个任务需要怎样做;第四个参数也是一个回调函数,表示这个任务完成后,应该怎样做;第五个参数是完成这个任务所需要的一些参数,一般来说传一个结构体进去就行,在实际的开发中,一不小心两次传入的这个参数地址一样了,如果传入的参数地址相同,会使用同一个线程处理,并且后一个参数会覆盖前一个参数,这是一个值得注意的问题。最后一个参数填为NULL就行了。当然,这个接口也可以用stpool_task_new和stpool_task_set_p两个接口来代替,我测试中发现两者貌似没有什么区别。

当然,如果你想取消某个任务,可以用stpool_task_delete接口,但是这个接口考虑到了安全的因素,一般如果任务里有循环(不满足某个条件就不会跳出循环),那么这个接口就会报错。

随时都可以用stpool_stat_print接口来打印某个线程池的所有状态。

libstpool也支持优先级任务,再次就不再赘述了,详情可以查看库中example文件夹的demo.c文件,优先级这块两个接口挺好用的。

也可以用stpool_suspend和stpool_resume两个接口来暂停和继续整个线程池中任务的进行。

最后,出于程序健壮性的考虑,需要用stpool_release来释放整个线程池的资源。(虽然站在服务器的角度来讲,这个接口永远都不会调用到,如果服务器崩溃,资源自然就释放掉了[擦汗])。

在整个服务器完成后,使用top -H -p +pid号即可查看相关线程。实际测试发现,这个库还是非常好用的。

共有 0 条评论

Top