2017-12-30 60 views
0

Qt C++ 프로그램이 있습니다. 메인 드라이버 MainWindowTCPClient 클래스가 있습니다. TCPClient 클래스는 원격 서버와 통신하고 TCP를 통해 일부 데이터를 전송하며 데이터 처리를 요청하고 서버에서 처리 된 데이터를 수신하는 데 사용됩니다. 내 TCPClient 클래스에서 QAbstractSocket 신호 disconnected을 사용하고 있습니다. 이 연결은 서버와의 연결이 끊어지면 발생합니다. 이 disconnect 신호 (ifDisconnected)를 처리하는 기능 (슬롯)에서는 MainWindowonCompletionCallback 기능이 호출됩니다. 이제 내 질문은 어떻게 실행 완료 후 TCPClient 실행의 다시 전송을 방지 할 수 있습니다. 다음은 문제를 설명하는 불완전한 코드입니다. Qt 신호가 출력 될 때 마지막으로 실행 된 기능으로 실행 제어가 전송되는 것을 어떻게 막을 수 있습니까?

void TCPClient::connectToServer(QJsonDocument requestJson) 
{ 
    // Removed code of other connect signals 
    connect(tcpSocket_, &QTcpSocket::disconnected, this, &TCPClient::ifDisconnected); 

} 

void TCPClient::ifDisconnected() 
{ 
    // Here the callback is called. After the callback finishes executing, I don't want execution to return to `TCPClient`. 
    onCompletionCallback_(); 
    return; 
} 

TCPClient.cpp

void MainWindow::on_connectButton_clicked() 
{ 
    std::function<void(void)> callback std::bind(&MainWindow::onCompletetionCallback, this); 
    tcpClient_ = new TCPClient(callback)->connectToServer(someData); 
} 

void MainWindow::onCompletetionCallback() 
{ 
    if(tcpClient_->isRequestSuccess()) 
    { 

     QJsonDocument responseJson = tcpClient_->getResponse(); 
     return; //When this finishes executing, I want to prevent the execution control to go back to TCPClient 
    } 
} 

mainwindow.cpp

는이 문제를 어떻게 해결 않습니다. QAbstractSocket은 연결이 가능한지 확인하기위한 유틸리티 기능을 제공하지 않기 때문에 신호 disconnected을 사용해야합니다.

답변

0

신호 처리기가 호출자에게 반환되지 않도록 할 수 없으며 그렇게해서는 안됩니다. 그렇지 않으면 호출 스택이 손상됩니다.

실제 질문은 (나를 위해)입니다 : 신호 처리기의 호출자는 무엇입니까?

무슨 뜻인지 이해하려면 Qt 문서를 읽어보십시오. QObject::connect()Qt::ConnectionType 특히주의하십시오. 수신기가 신호를 방출하는 스레드에 살고

경우, Qt는 :: DirectConnection이 사용됩니다

기본값은 의미 Qt::AutoConnection이다. 그렇지 않으면 Qt :: QueuedConnection이 사용됩니다. 연결 유형은 신호가 방출 될 때 결정됩니다.

Qt::DirectConnection

: 신호가 방출 될 때 슬롯을 즉시 호출

. 슬롯은 신호 스레드에서 실행됩니다. (내게)

가장 일반적인 경우 (엄격한 단일 스레딩 방식) 응답 데이터 또는 다른 위젯 수정 GUI 객체의 변경 요구 신호 처리기 (즉, 위젯 등)이다. 이 경우 Qt::DirectConnection, 즉 위젯 신호 이미 터는 내 신호 처리기의 호출자입니다.

신호를 내 보낸 위젯을 삭제하는 중 오류가 발생했습니다 (예 : 대화 상자의 닫기 버튼 이벤트 처리). – 나쁜 아이디어 : 호출 스택에서 보류중인 메소드 호출로 위젯을 삭제했습니다. 내 신호 처리기에서 돌아온 후 충돌이 발생했습니다. 호출자 메서드 (신호 이미 터)에 더 이상 인스턴스가 없거나, 즉 this이 무효화되었습니다. (그것은 당신이 앉아있는 사지를 톱질하는 것과 같습니다.) (Btw. deleteLater이 이것에 대한 하나의 해결책이 될 수 있습니다. 이것에 관해서는 SO: How delete and deleteLater works with regards to signals and slots in Qt?을 발견했습니다.)

코드 샘플

connect(tcpSocket_, &QTcpSocket::disconnected, this, &TCPClient::ifDisconnected); 

고려 나는 이것이 Qt::DirectConnection 의심.

다른 측면 : TCP 클라이언트 스레드에서 주 창 기능을 호출하는 것은 특별한주의가 필요한 사항입니다. 호출자는 TCP 클라이언트 스레드에있는 것이지만 (다른) GUI 스레드에 상주하는 오브젝트 (기본 창)를 처리합니다. 휴. GUI 쓰레드 자체가 이것을 사용한다면, 호출 된 함수에서 액세스되는 것은 모두 (뮤텍스가 가드되어야한다.)

그럼, 다른 옵션에 대해 :

Qt::QueuedConnection :

슬롯은 수신기의 스레드의 이벤트 루프 제어시 반환 호출됩니다. 슬롯은 수신자의 스레드에서 실행됩니다. 스레드 간의 통신

는 IMHO는 Qt::QueuedConnection은 안전한 방법 다음 TCP 클라이언트 수신기 객체로서 주어진 기본 창을 가정 GUI 스레드 (의 이벤트 루프의 각 항목에 발생 된 신호를 방사). GUI 스레드는 이벤트 루프를 처리하는 동안이 항목을 선택합니다. 이 경우 GUI 스레드의 이벤트 루프는 신호 처리기의 호출자입니다. TCP 클라이언트 스레드는 신호 요청을 보낸 후에 대기하지 않았지만 처리를 계속했습니다. 이 세 번째 옵션을 요구하지 않는 경우 활동하기 시작 :

Qt::BlockingQueuedConnection : Qt는 :: QueuedConnection과 동일

를 제외하고 슬롯 반환 될 때까지 신호 스레드 블록. 수신기가 신호 스레드에있을 경우이 연결을 사용하면 안됩니다. 그렇지 않으면 응용 프로그램이 교착 상태가됩니다. Qt::BlockingQueuedConnection

는 GUI 스레드까지 신호 송신기 (는 TCP 클라이언트)는 신호 처리기를 처리 할 수있다. (는 TCP 클라이언트 스레드가 GUI 스레드가 수신기신호 스레드 그대로 죽은 잠금에 대한 경고는 여기에 적용되지 않습니다.)

내가 추천 무엇을 조금 불확실 해요. 귀하의 응용 프로그램에 약간의 재 설계가 필요할 것 같지만 코드 샘플은 약간 불완전합니다.

가능한 용액 :

  1. 완성이 요구 될 때 방출되는 Qt를 신호 도입. MainWindow::onCompletetionCallback()Qt::BlockingQueuedConnection을 사용하여이 TCP 클라이언트 신호에 신호 처리기로 연결될 수 있습니다.

  2. 전송이 끝나면 TCP 클라이언트 스레드가 손상 될 수 있습니다. 그러나, 다른 스레드를 죽이는 스레드는 일반적으로 좋은 아이디어가 아닙니다. Qt가 이것을 "어떻게 처리하는지"는 잘 모르겠습니다. 따라서 더 나은 개념은 다음과 같습니다. 전송 끝이 인식되면 주 스레드는 TCP 클라이언트 스레드에 플래그를 지정하여 주 루프를 종료합니다. 신고는 예를 들어 std::atomic<bool> (또는 Qt에 머물러 계시고 자체 펜던트가 QAtomicInt 인 경우).TCP 클라이언트는 주 루프에서 또는 적어도 신호를 방출 한 후이 플래그를 확인하고 경우에 따라 종료합니다.

마지막 힌트 :

당신은 당신이 제대로 – 모든 신호 물건을 이해 내가 신호 처리기에 중단 점을 넣고 실행이 중지 될 때 호출 스택을 검사하여 내 이해를 확인 여부를 잘 모르는 경우

그 중단 점에서. 이것은 쉽고 직선적입니다 (마우스를 다루거나 & 드롭 이벤트 제외).