線程池初探(NTPL/pthread)
阿新 • • 發佈:2019-05-14
程序設計 run 時間 void () 這樣的 缺點 ++ static
最近計網課設選擇了寫一個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說到:”互斥量不是免費的。需要時間來加鎖解鎖。“
關於信號量和條件變量,且聽下回分解。
線程池初探(NTPL/pthread)