多线程在网络编程中作用重大,由于创建/销毁线程、线程间通信的开销小,目前很多网络服务器都是用多线程(线程池)的模式对外提供服务。linux上开发多线程程序多使用pthread库,本文主要讨论使用C++封装pthread库时可能出现的问题。
封装pthread库主要有两种思路,第一种是简单的封装接口,如下所示:
- class simple_thread_t {
- public:
- int start(void (*task)(void *), void *argp)
- {
- // do something else
- pthread_create(&_tid, NULL, task, argp);
- }
- int join()
- {
- // do something else
- pthread_join(_tid, NULL);
- }
- int detach()
- {
- pthread_detach(_tid);
- }
- // some other interfaces
- private:
- pthread_t _tid;
- };
simple_thread_t只是简单的封装了pthread库的接口,但需要创建线程时,跟使用pthread库一样,需要先实现好线程例程函数,然后传递给start,线程执行时需要依赖的数据可以通过参数传递。
第二种封装方式则是在内部做了参数传递的工作,充分利用C++继承的特性,如下代码。
- class common_thread_t {
- public:
- int start()
- {
- // do something else
- pthread_create(&_tid, NULL, _process, (void *)this);
- }
- int join()
- {
- // do something else
- pthread_join(_tid, NULL);
- }
- int detach()
- {
- pthread_detach(_tid);
- }
- // some other interfaces
- virtual void run() = 0;
- private:
- pthread_t _tid;
- static void *_process(void *argp)
- {
- common_thread_t *thread = (common_thread_t*)argp;
- thread->run();
- }
- };
用户要创建线程,只需继承common_thread_t,并实现run接口即可,用户可以在子类中定义额外的数据以供run接口使用,在实现子类后,创建子类实例并调用start即可创建线程。
在使用common_thread_t时,有个限制,在线程没有正式运行之前,common_thread_t的对象不能销毁,否则会出现pure virtual function called的错误, 因为在线程执行例程中需要调用run方法,而如果此时对象已销毁,则会找不到对应的虚函数。这个限制使得在需要创建需要detach线程时,new出来的common_thread_t对象,没有合适的时机来delete,因为不知道detach后的线程何时结束。
2011年11月22日补充:
为了解决堆上创建线程释放的问题,可以使用boost::shared_ptr,让thread的run函数thread对象的的shared_ptr,使得在线程函数执行的过程中thread对象一直不会释放,等其线程函数执行完后才会被释放。
实现参考:https://gist.github.com/1384659 (作者:陈硕)