11

私のアプリケーションには のインスタンスがありQTimer、そのtimeout()シグナルはメイン ウィンドウ オブジェクトのスロットに接続されているため、定期的に呼び出されます。スロットはカメラで写真を撮り、ディスクに保存します。

QTimerレシーバー(メインスレッドのウィンドウオブジェクト)が現在ビジーなときに(前の写真を撮って保存するなど)、信号が(実行される別のスレッドから)送信されるとどうなるか疑問に思っていました。前の呼び出しが終了した後、呼び出しはキューに入れられて実行されますか? 全体的なアイデアは、定期的に実行することですが、それらの呼び出しがキューに入れられ、制御がイベントループに戻ったときにランダムに呼び出されて混乱を引き起こす可能性はありますか? どうすれば回避できますか?理論的には、スロットはすぐに実行されるはずですが、ハードウェアに何らかの問題があり、停止したとしましょう。

このような状況では、呼び出しがキューに入れられるのではなくドロップされることを望みます。さらに便利なのは、呼び出しが発生したときに反応する (ユーザーに警告し、実行を終了する) 機能です。

4

4 に答える 4

6

現時点での他の回答には、関連するコンテキストがあります。ただし、知っておくべき重要なことは、タイマー コールバックが別のスレッドのスロットを通知している場合、その接続は QueuedConnection または BlockingQueuedConnection のいずれかであるということです。

したがって、タイマーを使用してある種の定期的な処理を実行しようとすると、受信オブジェクトが独自のスレッドにあるため、タイマーが起動してからスロットが実際に実行されるまでのタイミングに追加のジッターが発生します。独立したイベント ループを実行します。つまり、イベントがキューに入れられたときに他のタスクをいくつでも実行でき、それらのイベントの処理が完了するまで、ピクチャ スレッドはタイマー イベントを実行しません。

タイマーは、フォト ロジックと同じスレッドにある必要があります。タイマーをカメラ ショットと同じスレッドに配置すると、接続が直接になり、タイミング間隔の安定性が向上します。特に、写真のキャプチャと保存に例外的な時間がかかる場合があります。

間隔が 10 秒であると仮定すると、次のようになります。

  • タイマーを10秒にセット
  • タイマーの発火
  • 開始時間を節約する
  • 写真を撮る
  • 写真をディスクに保存します (奇妙な理由で 3 秒かかるとします)。
  • 10-(現在時刻 - 開始時刻)= 7 秒を計算します。
  • タイムアウトを 7 秒に設定する

ここでいくつかのロジックをセットアップして、スキップされた間隔を検出することもできます (操作の 1 つが完了するまでに 11 秒かかるとします...

于 2013-09-07T21:57:30.997 に答える
4

QTimerここでは、レシーバーがビジー状態のときに がどのように動作するかをいくつか実験した後、詳しく説明します。

実験用のソース コードは次のとおりです: (QT += testlibプロジェクト ファイルに追加)

#include <QtGui>
#include <QtDebug>
#include <QTest>

struct MyWidget: public QWidget
{
    QList<int> n;    // n[i] controls how much time the i-th execution takes
    QElapsedTimer t; // measure how much time has past since we launch the app

    MyWidget()
    {
        // The normal execution time is 200ms
        for(int k=0; k<100; k++) n << 200; 

        // Manually add stalls to see how it behaves
        n[2] = 900; // stall less than the timer interval

        // Start the elapsed timer and set a 1-sec timer
        t.start();
        startTimer(1000); // set a 1-sec timer
    } 

    void timerEvent(QTimerEvent *)
    {
        static int i = 0; i++;

        qDebug() << "entering:" << t.elapsed();
        qDebug() << "sleeping:" << n[i]; QTest::qSleep(n[i]);
        qDebug() << "leaving: " << t.elapsed() << "\n";
    }   
};  

int main(int argc, char ** argv)
{
    QApplication app(argc, argv);   
    MyWidget w;
    w.show();
    return app.exec();
}

実行時間が時間間隔より小さい場合

その後、予想どおり、タイマーは1秒ごとに着実に実行されます. 実行にかかった時間を考慮し、メソッドtimerEventは常に 1000 ミリ秒の倍数で開始します。

entering: 1000 
sleeping: 200 
leaving:  1201 

entering: 2000 
sleeping: 900 
leaving:  2901 

entering: 3000 
sleeping: 200 
leaving:  3201 

entering: 4000 
sleeping: 200 
leaving:  4201 

受信者が話し中だったために 1 クリックだけ逃した場合

n[2] = 1500; // small stall (longer than 1sec, but less than 2sec)

次に、ストールが終了した直後に次のスロットが呼び出されますが、後続の呼び出しは依然として 1000ms の倍数です

entering: 1000 
sleeping: 200 
leaving:  1200 

entering: 2000 
sleeping: 1500 
leaving:  3500 // one timer click is missed (3500 > 3000)

entering: 3500 // hence, the following execution happens right away
sleeping: 200 
leaving:  3700 // no timer click is missed (3700 < 4000)

entering: 4000 // normal execution times can resume
sleeping: 200 
leaving:  4200 

entering: 5000 
sleeping: 200 
leaving:  5200 

また、時間の蓄積により次のクリックも見逃された場合でも機能します。ただし、各実行で見逃されるクリックが 1 つだけである場合に限ります

n[2] = 1450; // small stall 
n[3] = 1450; // small stall 

出力:

entering: 1000 
sleeping: 200 
leaving:  1201 

entering: 2000 
sleeping: 1450 
leaving:  3451 // one timer click is missed (3451 > 3000)

entering: 3451 // hence, the following execution happens right away
sleeping: 1450 
leaving:  4901 // one timer click is missed (4901 > 4000)

entering: 4902 // hence, the following execution happens right away
sleeping: 200 
leaving:  5101 // one timer click is missed (5101 > 5000)

entering: 5101 // hence, the following execution happens right away
sleeping: 200 
leaving:  5302 // no timer click is missed (5302 < 6000)

entering: 6000 // normal execution times can resume
sleeping: 200 
leaving:  6201 

entering: 7000 
sleeping: 200 
leaving:  7201 

受信者が非常にビジーで、複数のクリックを逃した場合

n[2] = 2500; // big stall (more than 2sec)

クリックが 2 回以上失敗した場合にのみ、問題が発生します。実行時間は最初の実行とは同期されませんが、ストールが終了した瞬間と同期されます。

entering: 1000 
sleeping: 200 
leaving:  1200 

entering: 2000 
sleeping: 2500 
leaving:  4500 // two timer clicks are missed (3000 and 4000)

entering: 4500 // hence, the following execution happens right away
sleeping: 200 
leaving:  4701 

entering: 5500 // and further execution are also affected...
sleeping: 200 
leaving:  5702 

entering: 6501 
sleeping: 200 
leaving:  6702 

結論

ストールがタイマー間隔の 2 倍より長くなる可能性がある場合はDigikataのソリューションを使用する必要がありますが、それ以外の場合は必要なく、上記の簡単な実装でうまく機能します。次の動作が必要な場合:

entering: 1000 
sleeping: 200 
leaving:  1200 

entering: 2000 
sleeping: 1500 
leaving:  3500 // one timer click is missed 

entering: 4000 // I don't want t execute the 3th execution
sleeping: 200 
leaving:  4200 

その後、簡単な実装を引き続き使用して、それを確認するだけですenteringTime < expectedTime + epsilon。本当なら写真を撮り、嘘なら何もしない。

于 2013-09-09T06:13:37.983 に答える
3

答えはイエスです。QTimer とレシーバーが異なるスレッドにある場合、呼び出しはレシーバーのイベント キューに入れられます。また、写真の撮影や保存のルーチンが実行時間を浪費している場合は、イベントが大幅に遅れる可能性があります。しかし、これはすべてのイベントで同じです。ルーチンが制御をイベント ループに戻さない場合、GUI はハングします。以下を使用できます。

Qt::BlockingQueuedConnection QueuedConnection と同じですが、スロットが戻るまで現在のスレッドがブロックされます。この接続タイプは、エミッターとレシーバーが異なるスレッドにある場合にのみ使用してください。

しかし、ほとんどの場合、このような状況は、ロジックに問題があることを示しています。

于 2013-09-07T21:42:10.853 に答える
3

connect メソッドに接続タイプを使用して、Qt::(Blocking)QueuedConnectionすぐに発射される直接接続を回避できます。

別のスレッドがあるため、ブロッキング バージョンを使用する必要があります。ただし、レシーバー用の個別のスレッドを使用せずに直接呼び出しを避けたい場合は、ノンブロッキング バリアントを検討する必要があります。

詳細については、公式ドキュメントを参照してください。

便宜上、ドキュメントから:

Qt::QueuedConnection

制御が受信者のスレッドのイベント ループに戻ると、スロットが呼び出されます。スロットは受信側のスレッドで実行されます。

Qt::BlockingQueuedConnection

スロットが戻るまで現在のスレッドがブロックされることを除いて、QueuedConnection と同じです。この接続タイプは、エミッターとレシーバーが異なるスレッドにある場合にのみ使用してください。

おそらくあなたが書きたかったのは、直接接続するのではなく、キューに入れたいということです。

QCoreApplication::removePostedEvents ( QObject * receiver, int eventType )MetaCallこれらの重いタスクでキューが飽和状態になっている場合は、イベント タイプを使用したり、キューをクリーンアップしたりできます。また、フラグが設定されている場合は、これをスロットと通信して終了するために常にフラグを使用できます。

詳細については、次のフォーラムの議論も参照してください: http://qt-project.org/forums/viewthread/11391

于 2013-09-07T21:38:19.270 に答える