9

QT GUIスレッド(pthreadスレッド)とCコードのインターフェイスのコンテキストで、次の問題に遭遇しました。QTGuiスレッドを起動し、Cスレッドがパスを再開する前に、すべてのQT Guiスレッド内のグラフィカルオブジェクトが構築されており、それらは有効なQObjectです(CコードがQObject:connect()それらを呼び出すため)。

はじめに余談ですが、待機はpthread_cond_wait()+条件変数+Cスレッドの関連するミューテックスを介して行われます。

int isReady=0;
pthread_mutex_lock(&conditionReady_mutex);
while(isReady==0) {
    pthread_cond_wait(&conditionReady_cv, &conditionReady_mutex);
}
pthread_mutex_unlock(&conditionReady_mutex);

一方、QT Guiスレッドは、グラフィカルオブジェクトを作成し、次のように通知します。

pthread_mutex_lock(&conditionReady_mutex);
isReady=1;
pthread_cond_broadcast(&conditionReady_cv);
pthread_mutex_unlock(&conditionReady_mutex);

ご覧のとおり、基本的なものです。しかし、問題は次のとおりです。QtGuiスレッドでpthread_cond_broadcast()は、Cスレッドが確実にウェイクアップされるようにするために、を使用しています。はい、現在のアプリケーションでは、CスレッドとQt Guiスレッドしかなく、Cスレッドpthread_cond_signal()をウェイクアップするジョブを実行する必要があります(少なくとも1つのスレッドをウェイクアップすることが保証されており、Cスレッドが唯一のスレッドであるため) 1)。

しかし、より一般的なコンテキストでは、3つのCスレッドがあるが、そのうちの1つ(または2つ)をウェイクアップしたいとします。(2つの)特定のスレッド。どうすればそれを確認できますか?

を使用するpthread_cond_signal()と、3番目のスレッドだけがウェイクアップする可能性があり、関心のある1つのスレッドがウェイクアップされないため、ポイントを完全に見逃してしまいます。OTOH、すべてのスレッドを目覚めさせます。不要なスレッドも含めてpthread_cond_broadcast()、それはやり過ぎでしょう。

pthread_cond_signal()どのスレッドをウェイクアップするかを判断する方法はありますか?

または、ウェイクアップされるスレッドのグループ全体をより細かくするために、より多くの条件変数を導入する必要がありpthread_cond_broadcast()ますか?

ありがとうございました。

4

2 に答える 2

5

Yes, if you require a specific thread to be woken up then you either need to broadcast the wakeup, or use a separate condition variable for that thread.

于 2012-06-03T11:00:04.433 に答える
4

安全でないマナー

Qtおよび基になるC++で生成されたコードの動作が未定義であるため、別のスレッドからジェネリックQWidgetメソッドを直接呼び出すことは安全ではありません。それは核の先制攻撃を開始するかもしれません。あなたは警告されました。QWidget::update()したがって、Qtが提供する安全なスレッド間通信を使用しない限り、別のスレッドから、、、QLabel::setText(...)またはその他のそのようなメソッドを呼び出すことはできません。

間違い

// in a separate thread
widget->update();
widget->setText("foo");

// in a separate thread
// using QMetaObject::invokeMethod(widget, "update") is unoptimal
QCoreApplication::postEvent(widget, new QEvent(QEvent::UpdateRequest),
                            Qt::LowEventPriority);
QMetaObject::invokeMethod(widget, "setText", Q_ARG(QString, "foo"));

スレッド間通信について

一部のQObjectを含むスレッドと通信する簡単な方法には、静的QCoreApplication::postEvent()メソッドを介してスレッドにイベントを投稿することが含まれます。GUIスレッドは、多数のQObjectが存在するスレッドの一般的な例です。使用postEvent()は常にスレッドセーフです。このメソッドは、どのスレッドから呼び出されたかをまったく気にしません。

内部でを使用するさまざまな静的メソッドがQt全体に散在していますpostEvent()。彼らの調号は、彼らも静的になるということです!家族はそのQMetaObject::invokeMethod(...)ような例です。

異なるスレッドに存在するQObjectのスロットに信号を接続する場合、キュー接続が使用されます。このような接続では、信号が発信されるたびQMetaCallEventに、-が正しく推測された-を使用して、aが作成され、ターゲットQObjectに送信されますQCoreApplication::postEvent()

したがって、GUI以外のスレッドのQObjectからQWidget::update()スロットに信号を接続することは安全です。これは、内部的にそのような接続がpostEvent()信号をスレッドバリアを越えて伝播するために使用するためです。QWidget::update()GUIスレッドのイベントループはそれを取得し、ラベル上のへの実際の呼び出しを実行します。完全にスレッドセーフ。

最適化

スレッド間でシグナルスロット接続またはメソッド呼び出しを使用する場合の唯一の問題は、Qtが更新を圧縮する必要があることを認識していないことです。別のスレッドに接続されたシグナルを送信するQWidget::update()たび、または呼び出すたびinvokeMethodに、イベントキューにイベントが投稿されます。

文書化されていない場合、スレッド間更新を行う慣用的な方法は次のとおりです。

QApplication::postEvent(widget,
                        new QEvent(QEvent::UpdateRequest),
                        Qt::LowEventPriority);

UpdateRequestイベントはQtによって圧縮されます。いつでも、特定のウィジェットのイベントキューにそのようなイベントを1つだけ含めることができます。したがって、最初にそのようなイベントを投稿すると、ウィジェットが実際にイベントを消費してそれ自体を再描画(更新)するまで、後続のイベントは無視されます。

では、たとえば、さまざまなウィジェット設定呼び出しについてはどうQLabel::setText()でしょうか。どういうわけかそれらも圧縮できたら、きっとクールだろうか?はい、それは確かに可能ですが、パブリックQtインターフェイスに限定する必要があります。Qtはここでは明白なことを何も提供していません。

QMetaCallEvent重要なのは、別のイベントを投稿する前に、ウィジェットのイベントキューから保留中のイベントを削除することです。これは、特定のウィジェットへのキューに入れられたシグナルスロット接続が私たちからのみ来ていることが確実な場合にのみ安全です。GUIスレッド内のすべてが自動接続を使用し、GUIスレッドのイベントキューにイベントを送信せずにスロットを直接呼び出すようにデフォルト設定されるため、これは通常、安全な仮定です。

方法は次のとおりです。

QCoreApplication::removePostedEvents(label, QEvent::MetaCall);
QMetaObject::invokeMethod(label, "setText", Q_ARG(QString, "foo"));

パフォーマンス上の理由から、スロットの正規化された表現を使用していることに注意してください。余分なスペースはなく、すべてconst Type &が単純に変換されますType

注:私自身のプロジェクトでは、プライベートQtイベント圧縮APIを使用することがありますが、ここではそれを推奨しません。

追記

Cコードは呼び出すべきではありませんQObject::connect、私はそれが悪いデザインの兆候だと思います。CコードからQtコードに通信する場合は、静的コードを使用しますQCoreApplication::postEvent(...)(もちろん、Cに適切にラップ/公開されます)。他の方法で通信するために、スレッドでイベントキューを手動でコーディングできます。少なくとも、「イベントキュー」は固定構造になりますが、重要なのは、ミューテックスを使用してアクセスを保護することです。

悪い設計のもう1つの兆候は、GUIクラスの外部のコードが、そのクラス自体の個々のUIアイテムに依存していることです。UIクラス全体(たとえば、)に一連のシグナルとスロットを提供し、MainWindowそれらを関連するUI要素に内部的に転送する必要があります。connect()信号を送信することはできますが、スロットをスロットに送信することはできません。転送スロットを手動でコーディングする必要があります。

于 2012-06-04T14:06:53.200 に答える