2013-09-24 49 views
2

這是一個簡單的(也可能是天真的)線程池的實現。通知線程退出

我想知道是否,鑑於下面的方法,有一個很好的方式來主動通知線程池線程退出,而不是在每次超時後檢查布爾值mStopped

我明白,有更好的方法來實現一個線程池 - 但我仍然有興趣看看下面的代碼是否可以改進,而不會從根本上改變它。

我很高興聽到一般性建議......不一定修復下面的代碼......問題實際上是關於信號/等待線程,線程池只是爲了給出一些上下文。

我用gcc 4.4.6(所以下面的一些語法是有點過時了),而且代碼與g++ --std=c++0x main.cc -pthread

#include <vector> 
#include <mutex> 
#include <thread> 
#include <deque> 
#include <condition_variable> 
#include <functional> 

using namespace std; 

struct ThreadPool; 

struct Worker { 
    ThreadPool& mThreadPool; 
    Worker(ThreadPool& threadPool) : mThreadPool(threadPool) {} 
    void operator()(); 
}; 

struct ThreadPool { 
    vector<thread> mWorkers; 
    deque<function<void()>> mTasks; 
    bool mStopped; // not atomic 
    mutex mMutex; 
    condition_variable mCond; 

    ThreadPool() : mStopped(false) { 
    for (size_t i = 0; i < 5; i++) 
     mWorkers.push_back(thread(Worker(*this))); 
    } 

    ~ThreadPool() { stop(); } 

    void stop() { 
    if (!mStopped) { 
     mStopped = true; 
     for (auto it = mWorkers.begin(); it != mWorkers.end(); ++it) 
     if (it->joinable()) it->join(); 
    } 
    } 

    bool canBreakFromWait() const { return !mTasks.empty(); } 

    void enqueue(function<void()> f) { 
    if (mStopped) return; 
    unique_lock<mutex> lck(mMutex); 
    mTasks.push_back(f); 
    mCond.notify_one(); 
    } 
}; 

void Worker::operator()() { 
    while (true) { 
    unique_lock<mutex> lck(mThreadPool.mMutex); 
    mThreadPool.mCond.wait_for(lck, chrono::seconds(1), bind(&ThreadPool::canBreakFromWait, &mThreadPool)); 
    if (mThreadPool.mStopped) break; 
    auto task = mThreadPool.mTasks.front(); 
    mThreadPool.mTasks.pop_front(); 
    lck.unlock(); 
    task(); 
    } 
} 

void doWork(int data) {} 

int main() { 
    ThreadPool threadPool; 
    for (auto i = 0; i < 50; i++) 
    threadPool.enqueue(bind(doWork, i)); 
    threadPool.stop(); 
} 
+5

必須對所有對'mStopped'的訪問進行同步。 –

+0

謝謝詹姆斯。我同意他們應該是普遍的,但我不認爲這會影響給出的例子(即在這種情況下誤解mStopped不是災難性的)。如我錯了請糾正我。 –

+1

標準說未定義的行爲。在理論上,在更低的層面上,你可能永遠不會在其中一個線程中看到它「真實」。 (在實踐中......) –

回答

2

兩點意見彙編。

第一個是因爲mStopped在一個線程中被修改, 和在其他線程中被訪問,所有對它的訪問必須是 同步。目前,您的程序有未定義的 行爲。這是否會在實踐中出現問題,我不知道 ;先驗的是,編譯器可以在stop中推導出沒有其他的 線程訪問變量,並且在函數返回後調用stop線程的 中沒有訪問該線程,所以 禁止分配。

第二個是,在我有一份工作清單的情況下,我一般認爲最簡單的方法是創建一個特殊的作業,其中 終止該線程。標準線程似乎不具有 a thread_exit函數,但可以通過 輕鬆地模擬該函數,該函數可以使作業返回布爾值或使用異常。 stop函數然後鎖定互斥鎖,清空隊列,並且 將這些特殊終止作業之一插入到每個線程的隊列 中。

+0

鎖定隊列,排空隊列,然後放入終止工作,似乎是一個很好的方法。 –