线程池初探(NTPL/pthread)
最近计网课设选择了写一个ftp服务器,其中我想把服务器端写成多线程部分。由此接触了posix线程及其相关部分。相关阅读书籍:【1】现代操作系统 【2】posix多线程程序设计。
关于第二本是02年的,很久没出过新版了,NTPL与17年前的接口仍然大多一致,甚至是一样,由此引发了一些思考:现在那些花里胡哨的技术更新太快了,比如说java,c#,甚至c++每隔两年就要发布一个新的标准,然而这些底层接口仍然不变,学好了这些基础是不是更棒呢。
步入正题:我最初想的线程池是弹出式的:也就是有一个请求到来,进程就为该请求创建一个线程。代码如下:
class sample { public: void process(); }; template <typename T> class threadPool { public: threadPool(); void append(T &s); static void *worker(void *s); ~threadPool() { if(ifDestroy==false) { destroy(); } } void destroy() { pthread_mutex_destroy(&t); destroy=true; } private: pthread_t threads[3]; bool run = true; bool ifDestroy=false; }; template <typename T> void threadPool<T>::append(T& s) { pthread_t t; pthread_create(&t,nullptr,worker,&s); pthread_detach(t); } template <typename T> threadPool<T>::threadPool() {
} template <typename T> void *threadPool<T>::worker(void *s) { auto work= static_cast<sample *>(s); work->process(); return nullptr; } #endif //!THREAD_POOL_H
这里我把请求的地址作为参数传入线程,并将之设置为分离的(detached)。
关于线程的start_routine成员函数必须是静态的,这个还不清楚为什么,难道是跟c++的多态特性不兼容吗?
这个的缺点是显而易见的:若任务很小,只需要不多的指令,那么频繁的创建和回收销毁线程的开销则是巨大的。由此引入了我的线程池的下一个版本:
这回使用了mutex(mutual exclusion)互斥锁
class sample { public: void process(); }; template <typename T> class threadPool { public: threadPool(); void append(T &s); static void *worker(void *s); ~threadPool() { if(ifDestroy==false) { destroy(); } } void destroy() { pthread_mutex_destroy(&t); destroy=true; } private: std::queue<T *> tasks; pthread_t threads[3]; bool run = true; pthread_mutex_t t; bool ifDestroy=false; }; // template <typename T> // void threadPool<T>::append(T& s) // { // pthread_t t; // pthread_create(&t,nullptr,worker,&s); // pthread_detach(t); // } template <typename T> threadPool<T>::threadPool() : tasks() { pthread_mutex_init(&t, nullptr); for (int i = 0; i < 3; ++i) { pthread_create(&threads[i], nullptr, worker, this); pthread_detach(threads[i]); } } template <typename T> void threadPool<T>::append(T &s) { pthread_mutex_lock(&t); tasks.push(static_cast<T *>(&s)); printf("%u append.\n", pthread_self()); pthread_mutex_unlock(&t); } template <typename T> void *threadPool<T>::worker(void *s) { auto pool = static_cast<threadPool<T> *>(s); while (pool->run) { pthread_mutex_lock(&(pool->t)); if(pool->tasks.empty()) { pthread_mutex_unlock(&(pool->t));
pthread_yeild(); continue; } T *work = pool->tasks.front(); pool->tasks.pop(); pthread_mutex_unlock(&(pool->t)); work->process(); } return nullptr; } #endif //!THREAD_POOL_H
这个线程池用一个队列来先存储请求,用mutex保证其原子性(atomic),串行性。【2】的p:52说道:“互斥量的本质是在串行执行”。因为还不想涉及到条件变量和信号量,所以这里当工作线程检测到队列为空时就pthread_yield()暂时放弃cpu资源给其他线程,然而这样的不足仍有很多:比如当队列为空时,工作线程就会频繁地加锁解锁损耗计算机。【2】的p:52说到:”互斥量不是免费的。需要时间来加锁解锁。“
关于信号量和条件变量,且听下回分解。