1. 程式人生 > >線程池初探(NTPL/pthread)

線程池初探(NTPL/pthread)

程序設計 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)