2013-08-21 2 views
3

게임을 개발하는 중이고 멀티 스레딩 문제를 발견했습니다. 리소스를로드 할 때 이미 멀티 스레딩을 성공적으로 사용했습니다. 어느 시점에서 일부 스레드를 생성하고, 함수를 할당하고, 로딩 화면을 그릴 때 끝날 때까지 기다렸습니다.게임 루프에서 스레드 관리?

이제 함수를받을 때까지 유휴 상태가 될 때까지 기다릴 수있는 스레드를 만들고 싶습니다. 그들은 대략 다음과 같이하는 게임 루프에서 작동해야합니다 (난 그냥 쉽게 시각화를위한 이러한 함수 이름 해낸) :

std::thread t0,t1; 
while(gamerunning) 
{ 
    UpdateGame(); 
    t0.receiveFunc(RenderShadow); 
    t1.receiveFunc(RenderScene); 
    WaitForThreadstoFinishWork(); 
    RenderEverything(); //Only draw everything if the threads finished (D3D11's Deferred Context rendering) 
} 
t0.Destroy(); 
t1.Destroy(); 

내 렌더링 엔진 (테스트) 작업, 당분간되고, 내 게임 루프에 스레드를 만들었는데, 이는 렌더링 속도가 느려지 기 때문에 빠른 테스트조차도 끔찍한 방법입니다. 그건 그렇고, 나는 C++ 11의 라이브러리를 사용하고있다.

간단히 말하자면 게임 루프가 시작되기 전에 스레드를 만들고 나중에 게임 루프에서 스레드를 사용하여 누군가가 나를 도울 수 있기를 바랍니다. 옵션이라면 저수준의 스레딩에서 벗어나고 싶습니다.이 작업을 수행하는 가장 간단한 방법이 필요합니다.

+1

그래서, 당신은 임의의 작업을 받아 들일 수 작업자 스레드 풀을 원하는 (기능) 할 수 처형 됐지? 필요한만큼 많은 스레드와 함께 함수 ('queue > + 뮤텍스 + condition_variable' 또는 이와 비슷한 것)를 취하는 thread-safe 메시지 큐를 사용하십시오 (일반적으로 시스템의 CPU 코어의 두 배입니다. CPU를 포화시키고 자 함). – syam

+0

그러나 실제로는 메시지 대기열에 대한 귀하의 의견 중 두 번째 절반을 얻지는 못하지만 스레드 풀로 올바른 방향으로 설정했다고 생각합니다. 나는 그것을 들여다보고 돌아올 것이다. :) –

+1

이것은 매우 일반적인 패턴이므로 온라인에서 찾기가 너무 어려워서는 안됩니다. 그러나 당신에게 정확하게 설명하고 싶다면 그것은 많은 시간을 필요로 할 것입니다. 그래서 나는 단지 당신에게 힌트를주었습니다. ;) 정말로 알아낼 수 없다면, 여기에 ping을하면됩니다 (@ am 코멘트 추가). 제가 할 수있는 것을 보게 될 것입니다. :) – syam

답변

3

가장 최근의 의견에 이어 다음은 요청시 깨어나서 해당 작업을 실행 한 다음이를 관리하는 데 필요한 기능과 함께 다시 잠자기 상태로 전환되는 스레드의 구현 예입니다 (작업 완료를 기다린 후 종료의 경우 종료 대기).

기능 세트가 고정되어 있으므로 필요한만큼의 스레드 (즉, vector의 7 개)를 만들고 각각은 해당 작업을 수행해야합니다.

일단 디버그 cout을 제거하면 약간의 코드가 남아 있기 때문에 코드를 설명 할 필요가 없다고 생각합니다. (이것은 아주 단순한 IMHO입니다). 그러나 세부 사항에 대한 설명이 필요한지 물어 보는 것을 주저하지 마십시오. 여기

class TaskThread { 
public: 
    TaskThread(std::function<void()> task) 
     : m_task(std::move(task)), 
     m_wakeup(false), 
     m_stop(false), 
     m_thread(&TaskThread::taskFunc, this) 
    {} 
    ~TaskThread() { stop(); join(); } 

    // wake up the thread and execute the task 
    void wakeup() { 
     auto lock = std::unique_lock<std::mutex>(m_wakemutex); 
     std::cout << "main: sending wakeup signal..." << std::endl; 
     m_wakeup = true; 
     m_wakecond.notify_one(); 
    } 
    // wait for the task to complete 
    void wait() { 
     auto lock = std::unique_lock<std::mutex>(m_waitmutex); 
     std::cout << "main: waiting for task completion..." << std::endl; 
     while (m_wakeup) 
      m_waitcond.wait(lock); 
     std::cout << "main: task completed!" << std::endl; 
    } 

    // ask the thread to stop 
    void stop() { 
     auto lock = std::unique_lock<std::mutex>(m_wakemutex); 
     std::cout << "main: sending stop signal..." << std::endl; 
     m_stop = true; 
     m_wakecond.notify_one(); 
    } 
    // wait for the thread to actually be stopped 
    void join() { 
     std::cout << "main: waiting for join..." << std::endl; 
     m_thread.join(); 
     std::cout << "main: joined!" << std::endl; 
    } 

private: 
    std::function<void()> m_task; 

    // wake up the thread 
    std::atomic<bool> m_wakeup; 
    bool m_stop; 
    std::mutex m_wakemutex; 
    std::condition_variable m_wakecond; 

    // wait for the thread to finish its task 
    std::mutex m_waitmutex; 
    std::condition_variable m_waitcond; 

    std::thread m_thread; 

    void taskFunc() { 
     while (true) { 
      { 
       auto lock = std::unique_lock<std::mutex>(m_wakemutex); 
       std::cout << "thread: waiting for wakeup or stop signal..." << std::endl; 
       while (!m_wakeup && !m_stop) 
        m_wakecond.wait(lock); 
       if (m_stop) { 
        std::cout << "thread: got stop signal!" << std::endl; 
        return; 
       } 
       std::cout << "thread: got wakeup signal!" << std::endl; 
      } 

      std::cout << "thread: running the task..." << std::endl; 
      // you should probably do something cleaner than catch (...) 
      // just ensure that no exception propagates from m_task() to taskFunc() 
      try { m_task(); } catch (...) {} 
      std::cout << "thread: task completed!" << std::endl; 

      std::cout << "thread: sending task completed signal..." << std::endl; 
      // m_wakeup is atomic so there is no concurrency issue with wait() 
      m_wakeup = false; 
      m_waitcond.notify_all(); 
     } 
    } 
}; 

int main() 
{ 
    // example thread, you should really make a pool (eg. vector<TaskThread>) 
    TaskThread thread([]() { std::cout << "task: running!" << std::endl; }); 

    for (int i = 0; i < 2; ++i) { // dummy example loop 
     thread.wakeup(); 
     // wake up other threads in your thread pool 
     thread.wait(); 
     // wait for other threads in your thread pool 
    } 
} 

내가 무엇을 얻을 (실제 순서는 쓰레드 스케줄링에 따라 실행 실행에 따라 다릅니다) :

main: sending wakeup signal... 
main: waiting for task completion... 
thread: waiting for wakeup or stop signal... 
thread: got wakeup signal! 
thread: running the task... 
task: running! 
thread: task completed! 
thread: sending task completed signal... 
thread: waiting for wakeup or stop signal... 
main: task completed! 
main: sending wakeup signal... 
main: waiting for task completion... 
thread: got wakeup signal! 
thread: running the task... 
task: running! 
thread: task completed! 
thread: sending task completed signal... 
thread: waiting for wakeup or stop signal... 
main: task completed! 
main: sending stop signal... 
main: waiting for join... 
thread: got stop signal! 
main: joined! 
+0

이것은 두려운 것, 나는 너에게 충분히 감사 할 수 없다! 이런 경량 솔루션이 정말 필요했습니다. :) –

+1

반갑습니다. } 코드를 업데이트했는데 나머지 뮤텍스를 사용하기 때문에'wait()'와의 동시성 문제가 생겼다. 그래서'm_wakeup'을'std :: atomic '로 수정했다. 주 스레드가 작업 스레드의 발가락을 밟지 않도록 다른 변경 사항). – syam

+0

나는 그것을 시험해 보았다. 매력처럼 작동한다. 그리고 지금은 깨우고 기다릴 때 몇 초만 지나면 내 프로그램을 완전히 정지시키는 수정 된 코드를 시도했다. –