2016-08-23 198 views
-1

我目前有關於加入完成的std ::線程的問題。 我有一個簡單的消費類:加入完成std ::線程的死鎖

//Consumer.h 
class Consumer 
{ 
public: 
    //Ctor, Dtor 

    void Start(); 
    void Release(); 


private: 
    void Run(); 

    std::atomic<bool> m_terminate; 
    std::thread m_thread; 
}; 



//Consumer.cpp 
void Consumer::Start() 
{ 
    m_thread = std::thread(&EncodingProcessor::Run, this); 
} 

void Consumer::Release() 
{ 
    m_terminate = true; 
    if (m_thread.joinable()) 
     m_thread.join();  //Here is the deadlock 
} 

void Consumer::Run() 
{ 
    while (true) 
    { 
     if (m_terminate) 
      break; 


     //This queue is blocking, it is fed with events from an encoder 
     PMVR::HardwareEncoder::EventWithId currentEvent = m_hwEncoder.GetEncodeEvent(); 
     //If there is an event, again, wait for it, until the according frame is encoded 
     WaitForSingleObject(currentEvent.handle, INFINITE); 

     //Lock the encoded bitstream 
     PMVR::HardwareEncoder::BitStreamBufferInfo currentData = 
     m_hwEncoder.LockBitstream(currentEvent.id); 

     std::vector<unsigned char> tmp((unsigned char*)currentData.bitstreamPointer, 
     (unsigned char*)currentData.bitstreamPointer + currentData.bitstreamSize); 
     //And process it. 
     m_consumer->ProcessEncodingResult(currentData.bitstreamPointer, currentData.bitstreamSize); 
     m_hwEncoder.UnlockBitstream(currentEvent.id); 
    } 
} 

所以我可以啓動線程。線程做它應該做的。我可以結束該線程,因此Run()內的循環被破壞。但是,如果我想加入這個線程,我會遇到一個僵局。

我們不是在談論main()完成後發佈的線程結束。我可以Release()它通過按鍵,但它不會工作的時間。

編輯: Start()被稱爲是這樣的:

m_processorThread = new Consumer(*m_hwEncoder, 
    std::make_unique<FileSystemWriter>("file.h264")); 
m_processorThread->Start(); 

Release()被稱爲是這樣的:

if (glfwGetKey(handler->GetWindow(), GLFW_KEY_M) && !m_pressed) 
{ 
    m_pressed = true; 
    sessionAPI.Close(); 
} 

sessionAPI.close()只是調用Release()。而已。

EDIT2:

對不起,你是對的。我發佈的代碼目前正在工作... 所以這個問題似乎在Run()方法(更新,見上)。

所以我的誤解是,因爲在循環的頂部打破,它下面的整個東西不會被執行......看起來像GetEncodeEvent()產生了死鎖。但爲什麼?在線程沒有等待什麼的地方,有沒有一種打破整個循環的優雅方式?此外,事件的提供者還活着,所以應該有通知...

+3

你能告訴你如何調用'運行'和'釋放'? – NathanOliver

+0

一個完整的[mcve]會有很大的幫助。 – AndyG

+0

什麼是'EncodingProcessor :: Run()'在做什麼?這似乎是在另一個線程中運行的函數,因此我們需要查看該代碼以確定它爲什麼會死鎖。 – bnaecker

回答

1

我想這個問題,是在這裏:

{ 
    if (m_terminate) 
     break; 


    //This queue is blocking, it is fed with events from an encoder 
    PMVR::HardwareEncoder::EventWithId currentEvent = m_hwEncoder.GetEncodeEvent(); 
    //If there is an event, again, wait for it, until the according frame is encoded 
    WaitForSingleObject(currentEvent.handle, INFINITE); 

這都很好地設定m_terminatetrue,但你的線程不在那裏。它在WaitForSingleObject線上被阻止。

這是使用std::condition_variable的好參數。

例如:

#include <condition_variable> 
#include <mutex> 
#include <thread> 
#include <queue> 
#include <cassert> 

struct some_work {}; 

struct worker 
{ 

    void start() 
    { 
    assert(_stopped); 
    _stopped = false; 
    // memory fence happened here. The above write is safe 
    _thread = std::thread(&worker::run, this); 
    } 

    void stop() 
    { 
    auto lock = std::unique_lock<std::mutex>(_sc_mutex); 
    // this is a memory fence 
    assert(!_stopped); 
    _stopped = true; 
    // so is this 
    lock.unlock(); 
    // notify_all, in case someone adds a thread pool and does not look here! 
    // note: notify *after* we have released the lock. 
    _state_changed.notify_all(); 
    if (_thread.joinable()) 
     _thread.join(); 
    } 

    void post_work(some_work w) 
    { 
    auto lock = std::unique_lock<std::mutex>(_sc_mutex); 
    assert(!_stopped); 
    _workload.push(std::move(w)); 
    lock.unlock(); 
    // only notify one - we only added one piece of work. 
    _state_changed.notify_one(); 
    } 

    // allows a monitor to wait until all work is flushed before 
    // stopping if necessary 
    void wait() 
    { 
    auto lock = std::unique_lock<std::mutex>(_sc_mutex); 
    _maybe_stop.wait(lock, [this] 
         { 
          return should_stop() 
          or no_more_work(); 
         }); 
    } 

private: 

    void run() 
    { 
    std::unique_lock<std::mutex> lock(_sc_mutex); 
    _state_changed.wait(lock, [this] 
         { 
          return this->work_to_do() 
          or this->should_stop(); 
         }); 
    if (should_stop()) 
     return; 

    // there is work to do... 
    auto my_work = std::move(_workload.front()); 
    _workload.pop(); 
    lock.unlock(); 

    // do my work here, once we've locked. 

    // this is here for the wait() function above. 
    // if you don't want a wait(), you can dump this 
    lock.lock(); 
    if (no_more_work() or should_stop()) 
    { 
     lock.unlock(); 
     _maybe_stop.notify_all(); 
    } 

    } 

    bool work_to_do() const { return not _workload.empty(); } 
    bool no_more_work() const { return _workload.empty(); } 
    bool should_stop() const { return _stopped; } 

    std::mutex _sc_mutex; 
    std::condition_variable _state_changed; 
    std::condition_variable _maybe_stop; 

    std::queue<some_work> _workload; 

    std::thread _thread; 

    bool _stopped = true; 

}; 

int main() 
{ 
    worker w; 
    w.start(); 
    w.post_work(some_work()); 
    w.post_work(some_work()); 
    w.post_work(some_work()); 
    w.post_work(some_work()); 
    w.post_work(some_work()); 
    w.post_work(some_work()); 

    // do we want to ensure that all the work is done? 
    w.wait(); 
    w.stop(); 
} 
+0

http://stackoverflow.com/a/228797/257645 – kfsone

+1

@kfsone我不知道我關注? –

+0

我不明白的是:線程永遠不會等待很長時間。所以每秒鐘有60個事件。如果我監視m_terminate而不實際打破循環,它將如下所示:0,0,0,0,0,(按M將終止設置爲true)1,1,1,1,1等等。按下終止鍵後,除了這個消費者線程外,沒有什麼能夠停止工作了。因此,即使在那個時候,等待其他事件,它應該得到一個,然後繼續,注意改變終止並中斷。但事實並非如此。 – Christoph

1

你的代碼表示GetEncodeEvent阻塞。如果這是真的,那麼它可能讓你的代碼無限期地坐在那行代碼中,而沒有看到m_terminate中的變化。隨後,代碼可能無限期地在WaitForSingleObject處。

您可能需要考慮在整個功能中測試m_terminate

您不能中斷WaitForSingleObject,但你可以指定超時,只是包裹在一個循環

for (;;) { 
    if (m_terminate) 
     return; 
    auto res = WaitForSingleObject(currentEvent.handle, 20); 
    switch (res) { // check the return value 
     case WAIT_TIMEOUT: continue; 
     case WAIT_OBJECT_0: break; 
     default: error(...); 
    } 
} 

你的另一個選擇是創建的線程WaitEvent和使用WaitForMultipleObjects兩個手柄,並使用SetEventConsumer::Release中通知該線程。