2013-06-30 72 views
0

在我的Windows消息循環中,我調用一個std ::線程來計算一些東西(遊戲特定的東西)。我想禁止循環創建下一個線程,直到它計算出他必須計算的內容。現在我正在處理這這樣:不允許多次創建線程

if(!mIsCalculating) { 
    mIsCalculating = true; 
    std::thread th(Test::method, this); 
    th.detach(); 
} 

void Test::method() { 
    // ... 
    mIsCalculating = false; 
} 

但我不知道是否有性病庫中的現有解決方案,比如std :: invokeWhenLastDone? ;-)

+0

沒有鎖定,如果此代碼將在同一時間調用兩次,則可能創建2個線程。 如果你只從一個特定的線程調用它,這不是一個問題。 – asafrob

+0

std :: invokeWhenLastDoneWhatExactly? –

回答

2

而不是產卵每個任務一個新的線程,爲什麼不只是形成了幾個工作線程,然後使用std:packaged_taskstd::future併發計算的東西,而不產生一個新的線程的開銷

例如:

class Calculator { 
public: 
    Calculator() : m_bDoneFlag(false) { 
     for(auto& thread : m_arrayThreads) 
     { 
      thread = std::thread([this]{ 
       std::unique_lock<std::mutex> lockGuard(m_mutex, std::defer_lock); 

       while(!m_bDoneFlag) 
       { 
        lockGuard.lock(); 
        m_condTaskWaiting.wait(lockGuard, [this]{ return !m_queueTasks.empty(); }); 

        std::packaged_task<void*()> packagedTask = std::move(m_queueTasks.front()); 
        m_queueTasks.pop(); 

        lockGuard.unlock(); 

        // Execute task: 
        packagedTask(); 
       } 
      }); 
     } 
    } 

    ~Calculator() 
    { 
     m_bDoneFlag = true; 

     std::unique_lock<std::mutex> lockGuard(m_mutex); 
     m_queueTasks.emplace([]{ std::this_thread::sleep_for(std::chrono::milliseconds(100)); return nullptr; }); 
     m_queueTasks.emplace([]{ std::this_thread::sleep_for(std::chrono::milliseconds(100)); return nullptr; }); 
     lockGuard.unlock(); 
     m_condTaskWaiting.notify_all(); 

     for(auto& thread : m_arrayThreads) 
     { 
      thread.join(); 
     } 
    } 

    std::future<void*>       AddTask(std::function<void*()> funcToAdd) 
    { 
     std::packaged_task<void*()> packagedTask(funcToAdd); 
     std::future<void*> future = packagedTask.get_future(); 

     std::unique_lock<std::mutex> lockGuard(m_mutex); 
     m_queueTasks.emplace(std::move(packagedTask)); 
     lockGuard.unlock(); 
     m_condTaskWaiting.notify_one(); 

     return future; 
    } 

private: 
    std::mutex         m_mutex; 
    std::array<std::thread, 2>     m_arrayThreads; 
    std::queue<std::packaged_task<void*()>>  m_queueTasks; 
    std::condition_variable      m_condTaskWaiting; 
    std::atomic<bool>       m_bDoneFlag; 
}; 

然後,您可以使用此像這樣:

int main() 
{ 
    Calculator myCalc; 

    std::future<void*> future1 = myCalc.AddTask([]{ std::string* pszTest = new std::string("Test String"); return pszTest; }); 
    std::future<void*> future2 = myCalc.AddTask([]{ std::complex<float>* pcmplxTest = new std::complex<float>(5.0f, 10.5f); return pcmplxTest; }); 

    std::string* pszTest = reinterpret_cast<std::string*>(future1.get()); 
    std::complex<float>* pcmplxTest = reinterpret_cast<std::complex<float>*>(future2.get()); 

    std::cout << *pszTest << " and " << *pcmplxTest << std::endl; 

    delete pszTest; 
    delete pcmplxTest; 

    return 0; 
} 

顯然,這並不具備類型安全,我們會想,你可以改善型安全顯著如果您可以縮小返回類型值,您將始終需要防止必須返回指向void的指針。

+0

'AddTask'和'〜Calculator'都存取不帶'm_mutex'的'm_queueTasks',導致數據競爭。 – Casey

+0

@Casey謝謝你指出! –

+0

可能OP的另一個例子是閱讀一些混亂的'線程簡介'網站/頁面(因爲他們是軍團),它開始於'使用線程,你必須不斷創建它們並等待它們終止'。 –

1

據我所知,這種方法不存在。 但是,您可以通過讓單個線程永久運行並管理您在Test :: Method中需要的重複來解決您的問題。 當需要新的計算時,主循環可以通過使用std :: condition_variable來通知Test :: Method。

1

從技術上講,mIsCalculating應該是一個原子值,以便在兩個不同的線程中使用非原子變量時不會出現問題。除此之外,假設這在「UI線程」中使用,它只能從一個線程調用,所以應該是可以接受的。

還有幾種可選的解決方案,例如擁有一個永久運行的單個線程並將數據輸入到管道,消息隊列或使用事件發出「更多可用工作和類似事件」的信號,結果如下」