2017-11-22 32 views
2

개체가 소멸 될 때 끝내려는 std::thread을 포함하는 개체가 있습니다.QEventLoop을 std :: thread에 올바르게 임베드하는 방법은 무엇입니까?

최소한의 작업 코드 : 그것은 복잡 매우 빠르게된다

#include <thread> 
#include <future> 
#include <QEventLoop> 

struct Connector 
{ 
    Connector(std::string addr, std::function<void(std::string)> cb) 
    { 
     std::promise<void> barrier; 
     auto fut = barrier.get_future(); 

     m_worker = std::thread([addr, cb, &barrier]() 
     { 
      QEventLoop loop; 
      QTimer::singleShot(0, [this, &barrier, &loop] 
      { 
       m_quit = [&loop] { QTimer::singleShot(0, &loop, &QEventLoop::quit); }; 
       barrier.set_value(); 
      }); 

      MySocket s(addr, cb); 

      loop.exec(); 
     }); 

     fut.wait(); 
    } 

    ~Connector() 
    { 
     m_quit(); 
     m_worker.join(); 
    } 

    std::thread worker; 
    std::function<void()> m_quit; 
}; 

: 당신이이 exec() 입력 후에 만 ​​loopexit()를 호출 할 수 있습니다, 당신은 스레드의 loop 외부를 만들 수 없습니다.

이 루프에서 실행을 위해 대기중인 처리기에서 해제 한 세마포어가있는 해결 방법 만 있습니다. 세마포어가 해제되면 필자는 루프가 생성되어 실행 중이라는 것을 확신 할 수 있으므로 필요한 경우 quit() 메시지로 종료 될 수 있습니다.

더 간단한 방법이 없습니까?

+0

어떻게 '루프'로 무엇을합니까? 액세스 할 수 없으며 이벤트를 보낼 수 없습니다. – nwp

+0

@nwp,'exec()'의 직전에 객체를 생성합니다. 소켓과 같은 것, 연결할 IP 주소와 데이터를위한'std :: function' 콜백을 전달합니다. – Velkan

+1

소켓을 통해 이벤트 루프와 통신하는 방식이 표시되지 않습니다. 여러분이 ['QThread'] (http://doc.qt.io/qt-5/qthread.html)를 다시 구현하는 것처럼 보입니다. 이미 이벤트 루프 및 신호/슬롯 지원이 제공됩니다. – nwp

답변

0

아마도 QEventLoop의 unique_ptr에 대한 참조를 해당 포인터의 스레드 및 종료 호출 종료에 전달할 수 있습니다. 이처럼 :

#include <thread> 
#include <QEventLoop> 

struct Connector 
{ 
    Connector() 
    { 
     m_worker = std::thread([=]() 
     { 
      event_loop = std::make_unique<QEventLoop>(); 
      loop->exec(); 
     }); 
    } 

    ~Connector() 
    { 
     event_loop->exit(); 
     m_worker.join(); 
    } 

    std::unique_ptr<QEventLoop> event_loop; 
    std::thread worker; 
}; 
+0

'~ Connector()'는'event_loop = std :: make_unique ();'전에 실행될 수 있습니다. 그리고이 경우 NULL 포인터에서 충돌합니다. – Velkan

+0

참. 생성자에'std :: atomic_flag'를 추가하고 make_unique 호출 후에 스레드가 설정할 때까지 기다릴 수 있습니다. 그런 다음 소멸자에서'event_loop-> isRunning()'을 호출하여 종료 할 수 있는지 확인할 수 있습니다. – katrasnikj

+0

'~ Connector()'의'event_loop-> isRunning()'은'event_loop = std :: make_unique ();'과'std :: atomic_flag' 다음에 실행될 수 있지만'loop -> exec()'. 어떤 효과도없이'event_loop-> exit();'를 호출 할 것이고 ('isRunning()'을 체크하면 호출하지 않을 수도있다), 쓰레드는'loop-> exec();' 메인 쓰레드는'm_worker.join();'에 잠길 것이다. – Velkan

0

여기 스레드 생성자가 완료 시간에 의해 실행되고 있는지 확인하기 위해 katrasnikj의 대답과 std::promise docs에 따라 거기에 필자가 있습니다.

struct Connector 
{ 
    Connector() 
    { 
     std::promise<void> barrier; 
     auto fut = barrier.get_future(); 
     worker = std::thread([=](std::promise<void>&& barrier) 
     { 
      event_loop = std::make_unique<QEventLoop>(); 
      barrier.set_value(); 
      event_loop->exec(); 
     }, std::move(barrier)); 
     fut.wait(); 
    } 

    ~Connector() 
    { 
     QTimer::singleShot(0, event_loop.get(), &QEventLoop::quit); 
     worker.join(); 
    } 

    std::unique_ptr<QEventLoop> event_loop; 
    std::thread worker; 
}; 

하지만 당신은 자신의 이벤트 루프를 실행할 수이기 때문에, 대신 QThread을 사용하여 조사 할 수 있습니다.

+1

'barrier.set_value();'는 절대로 실행되지 않습니다. 'event_loop-> exec();'는 그 전에 차단됩니다. 그러나'std :: promise'에 대해서 언급 해 주셔서 감사합니다.세마포 대신 QSemaphore 솔루션에서 사용하겠습니다. 질문을 업데이트했습니다. – Velkan

+1

예, 나는 그것을 지금 잘못된 곳으로 옮겼습니다. – krzaq

+0

'QTimer :: singleShot()'호출에 문제가 있습니다 : '타이머는 QThread로 시작된 스레드에서만 사용할 수 있습니다.'오류. 따라서 실제로 EventLoop이 아닌 주 스레드에서 타이머를 시작할 수는 없습니다. – Velkan