2017-12-30 40 views
0

私はQt C++プログラムを持っています。私はメインドライバMainWindowTCPClientクラスを持っています。 TCPClientクラスは、リモートサーバーとの通信、TCPを介したデータの送信、データの処理の要求、およびサーバーから処理されたデータの受信に使用されます。私のTCPClientクラスでは、私はQAbstractSocketシグナルdisconnectedを使用しています。これは、サーバーとの接続が切断されたときに発生します。このdisconnect信号(ifDisconnected)を扱う関数(スロット)では、MainWindowonCompletionCallback関数が呼び出されます。今私の質問は、上記のonCompletionCallbackの実行が終了した後に、実行の送信をTCPClientに戻す方法をどうやって防ぐのかです。以下は、問題を説明するコードが不完全な場合です。Qt信号が出力されたときに最後に実行された関数に実行制御を送信しないようにするにはどうすればよいですか?

mainwindow.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 
    } 
} 

TCPClient.cpp

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; 
} 

どのように私はこの問題を解決するのです。 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スレッドに存在するオブジェクト(メインウィンドウ)に対処します。 Phew。 GUIスレッド自身もこれを使うならば、この呼び出された関数でアクセスされるものはすべて、ミューテックスがガードされている必要があります(exeptローカル変数)。

だから、何他のオプションについて:

Qt::QueuedConnection:コントロールは、受信側のスレッドのイベントループに戻るとき

スロットは呼び出されます。スロットは受信者のスレッドで実行されます。スレッド、私見との間の通信のため

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クライアントスレッドが破棄される可能性があります。しかし、別のスレッドを殺すスレッドは一般的には良いアイデアではありません。したがって、より良いコンセプトは次のようになります。送信の終了が認識されると、メインスレッドはTCPクライアントスレッドにフラグを立ててメインループを終了させます。フラグ付けは、例えば、 std::atomic<bool>(またはQtには自分のペンダントQAtomicIntがあります。TCPクライアントは、メインループで、または少なくともシグナルを出した後でこのフラグをチェックし、その場合には終了します。

最後のヒント:

あなたは正しく–すべての信号のものを理解するかどうかわからない場合、実行が停止したときに、私は、シグナルハンドラにブレークポイントを置くと、コールスタックを調べることによって、私の理解をチェックしますそのブレークポイントで。これは簡単で簡単です(あなたがマウスを操作しているか、&のドロップイベントをドラッグしている場合を除く)。

関連する問題