2016-08-30 4 views
1

類似の質問はたくさんありますが、適切な回答は見つかりませんでした。Qt:信号メインスレッド

私はサードパーティのライブラリを使用しています。

libのクラスの仮想メソッドのいくつかが呼び出されると、アプリケーションによって起動されたではないワーカースレッドから呼び出されます。このスレッドはQThreadではありません。

Qt :: DirectConnectionを使用してスロットに接続する場合のみ、このスレッドから送信できます。 SLOTのQObject :: sender()は常にNULLを返します。 例えば、deleteLater()を呼びたいと思っていますが、これはQThreadでのみスケジュールできます。

私はメインスレッドに戻る必要があると思いますが、メインスレッドでどのようにオブジェクトに信号を送ることができますか?

例:以下のメソッドが呼び出されると、サードパーティのライブラリで作成されたスレッドで同様の処理が行われます。

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

    return false; 
} 

次のような接続は、また、非QThreadコンテキスト内から構成されています

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

明示Qt::DirectConnectionがなければ、SipCall::slotMediaFinished()呼び出されることはありません。

+0

こんにちは、ワーカースレッドは別のAPIから来ているので、このAPIのメソッドを使用する必要があります。しかし、とにかくあなたはこの実装からシグナルを出しました。あなたのアプリケーションのスレッドへのポインタを保持し、スレッドがスレッドの終わりを知らせ、(あなたのQt MainThreadに)接続し、スロット内で削除させるための解決策でしょうか。 (もしあなたが既にMainthreadにいたら、deleteLaterまで待つ必要はありません)。または、待つ必要がある場合は、オブジェクトを削除しないでください。フラグを付けたいオブジェクトを削除したり削除したりできます。 –

+0

私は分かりません。ターゲットコード*が 'QThread'で実行されている限り、非' QThread'からのキューに入れられた接続にシグナルを送ることはOKです。どのように接続を作成していますか?コードを表示できますか? –

+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つのシナリオについて考えることができます:あなたが放射スレッドにあっても、イベントループからスロットを呼び出す場合、またはスレッドセーフスロットを呼び出すとき。 –

2

MediaPlayerインスタンスplayerは、アクティブなイベントループを持たないスレッドで作成されるという問題があります。あなたがコードに...

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

を変更すると

Qtのインフラストラクチャはplayerに関連するスレッドにイベントを掲載します。そのイベントを処理するイベントループがないため、ターゲットコードMediaPlayer::slotMediaFinishedは決して呼び出されません。

だから質問は...あなたはMediaPlayer::slotMediaFinishedを呼びたいと思っていますか?それがメインアプリケーションスレッドの場合は、実行する中contextようQCoreApplicationインスタンスに関連付けられているQThreadを使用します...

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

(あなたがqt5とC++ 11を使用していると仮定)を試みることができますラムダ。

編集:

あなたは、私が提案できる唯一の他のオプションが機能することができ、あなたのメインアプリケーションスレッドでQObject派生変数を持つことであるqt5または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に通知します。それはMediaPlayerのインスタンスをQObject::senderから識別し、そのslotMediaFinishedのメンバーを呼び出すことができるproxy::slotMediaFinishedを呼び出します。

+0

悲しいかな、私たちはここの曲線をよく裏付けており、まだQt4.7を使用しています。私たちは他のプラットフォーム(つまりQt)にXコンパイルする必要があるので、C++ 0xまたはC++ 11拡張(まだ)は使用できません。 – iwarv

+0

@iwarvこれは単なるコードを入力しなければならないことを意味します。ラムダは特別なことは何もしません。 –

+0

そのプロキシから再発行しませんか?クラス 'MediaPlayer'ではなく、' slotMediaFinished() 'メソッドを持つのは' SipCall'クラスです。確かに、プロキシソリューションはおそらく 'deleteLater()'の問題を解決しています(まだテストされていません) – iwarv

関連する問題