2016-08-23 2 views
-1

현재 완료된 std :: thread 결합과 관련하여 문제가 있습니다.완료된 std :: thread로 결합시 교착 상태가 발생했습니다.

//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()이 완료된 후에 공개 된 스레드 beeing에 대해 이야기하지 않습니다. 나는 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

'Run'과'Release' 호출 방법을 보여줄 수 있습니까? – 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); 

그것은 '모든 아주 잘 truem_terminate을 설정하지만, 스레드가 거기를보고 있지 않습니다. 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 (종료하려면 true로 설정하려면 M을 누릅니다), 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 모두 핸들을 사용하고 SetEvent을 사용하는 것입니다 루프에서 그것을 포장 할 수 있습니다 Consumer::Release에서 스레드에 알립니다.

관련 문제