2016-08-30 2 views
1

비슷한 질문이 많이 있지만 적합한 대답을 찾지 못했습니다.Qt : 신호 메인 스레드

타사 라이브러리를 사용합니다.

lib 클래스의 가상 메소드 중 일부가 호출되면 응용 프로그램에서이 아닌 이 아닌 작업자 스레드에서 호출됩니다. 이 스레드는 QThread가 아니며 앞으로있을 수 없습니다.

Qt :: DirectConnection을 사용하여 슬롯을 연결 한 경우에만이 스레드에서 방출 할 수 있습니다. 결과적으로 SLOT의 QObject :: sender()는 항상 NULL을 반환합니다. 예를 들어 deleteLater()를 호출하고 싶지만 QThread에서만 예약 할 수 있습니다.

메인 스레드로 되돌아 가야한다고 생각하지만 어떻게 메인 스레드에서 오브젝트에 신호를 보낼 수 있습니까?

예제 : 아래 메소드가 호출 될 때 타사 라이브러리에서 생성 된 스레드에서 수행됩니다. 명시 적 Qt::DirectConnection없이

/*virtual*/ void 
SipCall::state_answer_call::onEntering(SipCall& ref) 
{ 
    ... 
    MediaPlayer* player = new MediaPlayer; 
    ref.connect(player, SIGNAL(sigFinished()), SLOT(slotMediaFinished()), Qt::DirectConnection); 
    ... 
} 

SipCall::slotMediaFinished()가 호출되지 않습니다 다음과 같이

/*virtual*/ bool MediaPlayer::onEof() 
{ 
    stopTransmit(); 
    emit sigFinished(); // slots only called if bound using Qt::DirectConnection 
    deleteLater();  // dtor is never called 

    return false; 
} 

연결은 또한 비 QThread의 컨텍스트 내에서 이루어집니다.

+0

안녕하세요, 작업자 스레드가 다른 API에서 나왔으므로이 API의 메소드를 사용해야합니다. 그러나 여하튼 당신은이 구현에서 신호를 낼 수있었습니다. 그것은 귀하의 응용 프로그램에서 스레드에 대한 포인터를 잡고, 스레드가 스레드의 신호를 보내고, Qt MainThread에 연결하고, 슬롯에서 삭제하는 솔루션일까요? (만약 당신이 이미 Mainthread에 있다면 deleteLater까지 기다릴 필요가 없습니다.) 또는 기다려야 할 경우 개체를 삭제하지 말고 플래그를 지정한 개체를 삭제하십시오. –

+0

나는 내가 이해하고 있는지 잘 모르겠다. 'QThread'가 아니라면 QThread가 아닌 큐에있는 큐에 신호를 보내면됩니다. - QThread에서 타겟 코드 *가 실행되는 한. 어떻게 연결을 만들고 있습니까? 코드를 보여줄 수 있습니까? –

+0

@ G.M. 연결은 일반적인 QObject :: connect() 변형입니다. 특별한 것은 없습니다. 하지만 당신의 의견은 아마 그 자체가 다른 라이브러리 스레드 내에서 발생한다고 생각하게 만들었습니다. 그래서 디버그를 추가하고 이것을 확인했습니다 : 연결은 다른 (Qt 아님) 작업자 스레드 내에서 이루어집니다. – iwarv

답변

1

lib 클래스의 가상 메소드 중 일부가 호출되면 응용 프로그램에서 시작하지 않은 작업자 스레드에서 호출됩니다. 이 스레드는 QThread가 아니며 앞으로있을 수 없습니다.

오해의 소지가 있습니다. 그렇지 않습니다. 신호를 방출하는 스레드는 중요하지 않습니다. QThread을 사용하여 시작하지 않아도됩니다.

실제로 C 콜백에서 신호를 내보내는 것은 멀티 스레드 C 콜백 API를 Qt에 인터페이스하는 관용적 인 방법입니다. 그것은 아무런 노력없이 일하기위한 것입니다.

Qt :: DirectConnection을 사용하여 슬롯을 연결 한 경우에만이 스레드에서 방출 할 수 있습니다.

사실이 아닙니다. 신호와 슬롯이 다른 스레드에있을 때 연결할 슬롯/펑터가 스레드 안전 인 경우에만 직접 연결을 사용할 수 있습니다. 네가 의심 스럽다면 이 아니라 직접 연결을 사용해야한다. 자동 연결은 사용자가 필요로하는 것과 정확히 일치합니다.

수신 SipCall 인스턴스가 실행중인 이벤트 루프가있는 스레드에 없기 때문에 작동하지 않습니다. 그런 스레드로 이동하거나 the G.M.'s answer to this question에서 제안 된대로 주 스레드에있는 펑크 펑터를 사용해야합니다.

썽크 펑터의 문제점은 메서드를 호출하는 개체의 스레드 소유권을 난독 화한다는 것입니다. 대체로 SipCall 메서드는 스레드로부터 안전하지 않습니다. 이므로 주 스레드에서 호출하여 작업을 중단 할 수 있습니다. 플레이어를 응용 프로그램 스레드로 이동하고 해당 스레드에서만 사용되도록하는 것이 가장 안전합니다. 그러면 펑크 펑터는 필요 없습니다.

특정 스레드에서 어떤 코드 (예 : 펑터)가 실행되고 있는지 알아 보려면 this question을 참조하십시오.

+0

감사합니다. @ kuba-ober,하지만 당신은 분명히하실 수 있습니다; 부주의로'SipCall'과'MediaPlayer' 클래스 사이에 portmanteau를 만들었습니다. 메인 쓰레드로 옮겨야하는 클래스는 무엇입니까? – iwarv

+0

페이지 새로 고침 :) 모든 경우에 수신 클래스에 관심이 있습니다. 송신자 클래스의'thread()'는 중요하지 않습니다. 왜냐하면 자동 연결은 항상 신호를 방출 할 때 발신자 스레드를 검사하기 때문입니다. * 연결을 설정할 때 * 아닙니다 *. 자동 연결이 일반적으로 좋은 이유이기도하며 특별한 경우에 연결 유형을 재정의해야합니다. 내 머리 꼭대기에서 나는 두 가지 시나리오 만 생각할 수 있습니다. 방출하는 스레드에 있어도 이벤트 루프에서 슬롯을 호출 할 때 또는 스레드 안전 슬롯을 호출 할 때입니다. –

2

MediaPlayer 인스턴스 player은 활성 이벤트 루프가없는 스레드에서 생성되는 것이 문제입니다.

당신이 코드를 변경

...

ref.connect(player, SIGNAL(sigFinished()), SLOT(slotMediaFinished()), Qt::QueuedConnection); 

Qt는 인프라 player에 관련하는 thread에 이벤트를 게시 할 예정입니다. 해당 이벤트를 처리 할 이벤트 루프가 없으므로 대상 코드 MediaPlayer::slotMediaFinished은 절대로 호출되지 않습니다.

그럼 어떤 질문을 하시겠습니까 ... MediaPlayer::slotMediaFinished에 전화를 걸고 싶습니까? 그것의 경우 주요 응용 프로그램은

ref.connect(player, &MediaPlayer::sigFinished, QCoreApplication::instance(), 
    [&]() 
    { 
    player->slotMediaFinished(); 
    }, 
    Qt::QueuedConnection); 

이를 실행할에서 contextQCoreApplication 인스턴스와 연관된 QThread을 사용하는 것입니다 ... 당신이 (당신이 qt5 및 C++ (11)를 사용하는 가정)을 시도 할 수 스레드 람다.

편집 :

당신은 당신이 qt5 또는 내가 제안 할 수있는 유일한 다른 옵션은 역할을 할 수있는 기본 응용 프로그램 스레드에 QObject 파생 변수를하는 것입니다 C++ (11)를 사용할 수 없습니다 언급했듯이 player에 대한 프록시 컨텍스트 ...

class proxy: public QObject { 
    Q_OBJECT; 
public slots: 
    void slotMediaFinished() 
    { 
     if (MediaPlayer *player = dynamic_cast<MediaPlayer *>(sender()) { 
     player->slotMediaFinished(); 
     } 
    } 
}; 

응용 프로그램 스레드에서 위의 인스턴스를 생성 한 다음 connect 문이 될 것 ... 같은

ref.connect(player, SIGNAL(sigFinished()), &proxy_instance, SLOT(slotMediaFinished()), Qt::QueuedConnection) 

여기서 proxy_instance the, er, proxy 인스턴스.이제 sigFinished 신호가 방출되면 Qt는 이벤트를 proxy_instance에 게시합니다. 그러면 proxy::slotMediaFinished이 호출되어 QObject::sender에서 MediaPlayer 인스턴스를 식별하고 해당 slotMediaFinished 멤버를 호출 할 수 있습니다.

+0

아아, 우리는 여기서 커브를 잘 따라 잡고 있으며 여전히 Qt4.7을 사용하고 있습니다.우리는 다른 플랫폼 (그러므로 Qt)에 대해 X- 컴파일해야하기 때문에 C++ 0x 또는 C++ 11 확장을 사용할 수 없습니다 (아직). – iwarv

+0

@iwarv 즉, 더 많은 상용구 코드를 입력해야한다는 것입니다. 람다는 특별한 일을하지 않습니다. –

+0

해당 프록시에서 다시 발광하지 않겠습니까? 클래스'MediaPlayer'가 아니라'slotMediaFinished()'메소드를 가진 클래스'SipCall'입니다. 틀림없이, 프록시 솔루션은 아마도'deleteLater()'문제를 해결할 것입니다. (아직 테스트하지 않은 상태입니다.) – iwarv