2015-04-21 171 views
12

假設我有一個應該定期執行一些任務的線程,但是這個時間段是每小時6次每小時12次(每5分鐘),我經常看到控制線程的代碼循環用is_running標誌,它被選中的每一個迴路,這樣的:停止長時間睡眠線程

std::atomic<bool> is_running; 

void start() 
{ 
    is_running.store(true); 
    std::thread { thread_function }.detach(); 
} 

void stop() 
{ 
    is_running.store(false); 
} 

void thread_function() 
{ 
    using namespace std::literals; 
    while (is_running.load()) 
    { 
     // do some task... 
     std::this_thread::sleep_for(5min); 
    } 
} 

但如果stop()函數被調用,讓我們說,start()後1毫秒的線程會活着299999額外毫秒,直到醒來,檢查旗幟,並死亡。

我的理解是否正確?如何避免保持活着(但睡眠)應該已經結束的線程?我的最好的方法到現在爲止是這樣的:

void thread_function() 
{ 
    using namespace std::literals; 
    while (is_running.load()) 
    { 
     // do some task... 
     for (unsigned int b = 0u, e = 1500u; is_running.load() && (b != e); ++b) 
     { 
      // 1500 * 200 = 300000ms = 5min 
      std::this_thread::sleep_for(200ms); 
     } 
    } 
} 

是否有髒更小,更簡單的方式來實現這一目標?

+1

_每小時(每5分鐘)_6次,每小時12次或每10分鐘一次? :) –

+0

@AlexandreLavoie多麼失敗!謝謝,我會糾正它! :) –

+3

http://en.cppreference.com/w/cpp/thread/condition_variable,看第一句話。而不是睡一段固定的時間,你在這段時間內進入一個可信的等待狀態,這樣其他線程仍然可以中斷你 – stijn

回答

12

使用條件變量。您等待條件變量 5分鐘過去。請記住檢查虛假喚醒。

cppreference

我無法找到如何在一兩分鐘谷歌搜索的使用條件變量的好堆棧溢出後。棘手的部分是意識到wait可以通過5分鐘過去或者發送信號而被喚醒。處理這個問題的最簡單的方法是使用lambda的wait方法來檢查喚醒是否「好」。

here是cppreference上的一些示例代碼,它使用帶有lambda的wait_until。 (具有λ的wait_for相當於具有λ的wait_until)。我稍微修改了一下。

這裏是一個版本:

struct timer_killer { 
    // returns false if killed: 
    template<class R, class P> 
    bool wait_for(std::chrono::duration<R,P> const& time) const { 
    std::unique_lock<std::mutex> lock(m); 
    return !cv.wait_for(lock, time, [&]{return terminate;}); 
    } 
    void kill() { 
    { 
     std::unique_lock<std::mutex> lock(m); 
     terminate=true; // should be modified inside mutex lock 
    } 
    cv.notify_all(); // it is safe, and *sometimes* optimal, to do this outside the lock 
    } 
    // I like to explicitly delete/default special member functions: 
    timer_killer() = default; 
    timer_killer(timer_killer&&)=delete; 
    timer_killer(timer_killer const&)=delete; 
    timer_killer& operator=(timer_killer&&)=delete; 
    timer_killer& operator=(timer_killer const&)=delete; 
private: 
    std::condition_variable cv; 
    mutable std::mutex m; 
    bool terminate = false; 
}; 

live example。您可以在共享位置創建timer_killer。客戶端線程可以wait_for(time)。如果它返回false,這意味着你在等待完成之前死亡。

控制線程只是調用kill(),並且所有人在wait_for獲得false返回。

請注意,有一些爭用(互斥鎖),所以這不適合無限的線程(但很少有東西)。考慮使用調度程序,如果您需要有無限數量的任務以任意延遲運行,而不是每個延遲重複任務的完整線程 - 每個真實線程超過了所使用的系統地址空間的兆字節數量(僅用於堆棧) 。

-1

是,通過std::mutexstd::lock_guardstd::conditional_variable手段:

std::mutex mtx; 
std::conditional_variable cv; 

void someThreadFunction(){ 
    while(!stopThreadFlag){ 
    std::lock_gurad<std::mutex> lg(mtx); 
    cv.wait_for(lg,SOME_ITERVAL,!stopThreadFlag || wakeTheThreadVariable); 
    //the rest here 
    } 
} 
+0

您的代碼無法處理虛假喚醒。 – Yakk

+1

這不會編譯。 – Barry

+0

你有沒有包含所有正確的頭像? –

2

有你能做到這兩種傳統方式。

您可以對條件變量使用定時等待,並讓其他線程發出信號指示您的定期線程在醒來時死亡。

或者你也可以在管道上用poll作爲超時而不是睡覺。然後,您只需向管道寫入一個字節,該線程就會喚醒並退出。