2016-02-27 47 views
0

我試圖停止多個工作線程使用std::atomic_flag。從Issue using std::atomic_flag with worker thread開始了以下工作:std :: atomic_flag停止多個線程

#include <iostream> 
#include <atomic> 
#include <chrono> 
#include <thread> 

std::atomic_flag continueFlag; 
std::thread t; 

void work() 
{ 
    while (continueFlag.test_and_set(std::memory_order_relaxed)) { 
     std::cout << "work "; 
     std::this_thread::sleep_for(std::chrono::milliseconds(10)); 
    } 
} 

void start() 
{ 
    continueFlag.test_and_set(std::memory_order_relaxed); 
    t = std::thread(&work); 
} 

void stop() 
{ 
    continueFlag.clear(std::memory_order_relaxed); 
    t.join(); 
} 

int main() 
{ 
    std::cout << "Start" << std::endl; 
    start(); 
    std::this_thread::sleep_for(std::chrono::milliseconds(200)); 
    std::cout << "Stop" << std::endl; 
    stop(); 
    std::cout << "Stopped." << std::endl; 

    return 0; 
} 

試圖改寫成多個工作線程:

#include <iostream> 
#include <atomic> 
#include <chrono> 
#include <thread> 
#include <vector> 
#include <memory> 

struct thread_data { 
    std::atomic_flag continueFlag; 
    std::thread thread; 
}; 

std::vector<thread_data> threads; 

void work(int threadNum, std::atomic_flag &continueFlag) 
{ 
    while (continueFlag.test_and_set(std::memory_order_relaxed)) { 
     std::cout << "work" << threadNum << " "; 
     std::this_thread::sleep_for(std::chrono::milliseconds(10)); 
    } 
} 

void start() 
{ 
    const unsigned int numThreads = 2; 

    for (int i = 0; i < numThreads; i++) { 
     //////////////////////////////////////////////////////////////////// 
     //PROBLEM SECTOR 
     //////////////////////////////////////////////////////////////////// 
     thread_data td; 
     td.continueFlag.test_and_set(std::memory_order_relaxed); 

     td.thread = std::thread(&work, i, td.continueFlag); 

     threads.push_back(std::move(td)); 
     //////////////////////////////////////////////////////////////////// 
     //PROBLEM SECTOR 
     //////////////////////////////////////////////////////////////////// 
    } 
} 

void stop() 
{ 
    //Flag stop 
    for (auto &data : threads) { 
     data.continueFlag.clear(std::memory_order_relaxed); 
    } 
    //Join 
    for (auto &data : threads) { 
     data.thread.join(); 
    } 
    threads.clear(); 
} 

int main() 
{ 
    std::cout << "Start" << std::endl; 
    start(); 
    std::this_thread::sleep_for(std::chrono::milliseconds(200)); 
    std::cout << "Stop" << std::endl; 
    stop(); 
    std::cout << "Stopped." << std::endl; 

    return 0; 
} 

我的問題是「問題部門」上面。即創建線程。我無法圍繞如何實例化線程並將變量傳遞給工作線程。

現在的錯誤是引用此行threads.push_back(std::move(td));,錯誤號爲Error C2280 'thread_data::thread_data(const thread_data &)': attempting to reference a deleted function

嘗試使用的unique_ptr這樣的:

 auto td = std::make_unique<thread_data>(); 
     td->continueFlag.test_and_set(std::memory_order_relaxed); 

     td->thread = std::thread(&work, i, td->continueFlag); 

     threads.push_back(std::move(td)); 

在行td->thread = std::thread(&work, i, td->continueFlag);給出錯誤std::atomic_flag::atomic_flag(const std::atomic_flag &)': attempting to reference a deleted function。我從根本上誤解了std :: atomic_flag的使用嗎?它確實既不可移動也不可複製?

+0

我沒有閱讀代碼,所以這可能不適用。但'std :: atomic_flag'是相當低的水平,使用起來有點棘手。在我看來,'std :: atomic '在這裏更合適。它看起來就像普通的'bool':'if(my_flag)...'和'my_flag = true;'和'my_flag = false;'。 –

+0

根據標準'std :: atomic_flag'保證是無鎖的。一個簡單的工作線程測試增加一個由'while(continueFlag)'std :: atomic_flag包裹的計數器比'std :: atomic '快100%以上。鑑於這是一個綜合性的例子,它仍然表明,如果您經常對旗幟進行輪詢,旗標版本會更好。我傾向於在半關鍵部分進行民意調查,以瞭解工作是否被取消。 – Sheph

+0

除非你是一個非常時髦的系統'std :: atomic '將被鎖定。我非常懷疑那個和'std :: atomic_flag'之間的實際速度差別會很明顯。如果您的應用程序的性能受檢查原子變量的速度限制,那麼它沒有做任何實際工作,需要重新設計。 –

回答

0

我是差不多那裏用我的unique_ptr解決方案。我只是需要通過呼叫作爲一個std :: REF()這樣:

std::vector<std::unique_ptr<thread_data>> threads; 

void start() 
{ 
    const unsigned int numThreads = 2; 

    for (int i = 0; i < numThreads; i++) { 
     auto td = std::make_unique<thread_data>(); 
     td->continueFlag.test_and_set(std::memory_order_relaxed); 
     td->thread = std::thread(&work, i, std::ref(td->continueFlag)); 

     threads.push_back(std::move(td)); 
    } 
} 

然而,薩姆的啓發以上我也想通一個非指針方式:

std::vector<thread_data> threads; 

void start() 
{ 
    const unsigned int numThreads = 2; 

    //create new vector, resize doesn't work as it tries to assign/copy which atomic_flag 
    //does not support 
    threads = std::vector<thread_data>(numThreads); 
    for (int i = 0; i < numThreads; i++) { 
     auto& t = threads.at(i); 
     t.continueFlag.test_and_set(std::memory_order_relaxed); 
     t.thread = std::thread(&work, i, std::ref(t.continueFlag)); 
    } 
} 
0

你的第一種方法實際上更接近事實。問題在於它將對本地for循環範圍內的對象的引用作爲參數傳遞給每個線程。但是,當然,一旦循環迭代結束,該對象超出範圍並被破壞,從而使每個線程都引用一個被銷燬的對象,導致未定義的行爲。

在創建線程後,沒有人關心您將對象移動到std::vector的事實。線程接收到對本地作用域對象的引用,這就是它所知道的。故事結局。

首先將對象移動到矢量中,然後傳遞給每個線程對std::vector中的對象的引用也不起作用。只要該載體在內部重新分配,作爲其自然增長的一部分,你就會陷入同樣的​​困境。

需要做的事情是在實際啓動任何std::thread之前首先創建整個threads陣列。如果宗教上遵循RAII原則,那隻不過是簡單地致電std::vector::resize()

然後,在第二個循環中,遍歷完全烹飪的threads數組,然後爲數組中的每個元素生成一個std::thread

+0

我不確定這是否正確,但'threads.push_back(std :: move(td));'應該調用'push_back'作爲右值引用調用,因此應該調用默認的移動構造函數。因此,從對象移出是否超出範圍無關緊要。但是我發現的問題是'std :: atomic_flag'既不是移動的也不是可複製的(畢竟這是有道理的)。你的想法與resize()也不起作用,因爲它也**調用移動或複製任務,因爲它的內存被重新定位。但一個矢量的新副本的作品。看到我的答案。 – Sheph

+0

@Sheph - 它肯定很重要,因爲在原始版本中,線程在移動之前接收對移動的對象的引用。無論你如何去移動它,線程都不在乎。它獲得對移動對象的引用,並且隨後移動對引用沒有影響,因此每個線程都會繼續嘗試訪問被破壞的對象。未定義的行爲。你的最終解決方案是正確的 –