4

そのため、ビデオをリアルタイムで処理するプログラムに取り組んでおり、スレッドが互いに「ブロック」するという問題が発生しています。

私のシステムは、ほとんど次のように設定されています。

         DataSourceThread
             /      \
            /        \
           /          \
     Receiver       Receiver
         /              \
        /                \ 
       /                  \
 Processor1            Processor2

(これらのクラスはすべて QThread を拡張しています。)

そのため、DataSourceThread はビデオストリームからフレームをフェッチし、フレームを含む信号をレシーバーに送信します。接続タイプ: Qt::DirectConnection

レシーバーは基本的に DataSourceThread によって送信されたフレームを受信し、プロセッサが前のフレームの処理を完了すると、フレームを含む信号をプロセッサに送信します。接続タイプ: Qt::QueuedConnection。 プロセッサが前のフレームの処理を完了していない場合、信号を送信せずに戻ります (フレームをスキップします)。

これが機能するかどうかをテストするために、Processor1 がフレームを受信したときにメッセージを出力し、Processor2 がQThread::sleep(3);を実行しただけです。メッセージを印刷します。

(レシーバーは、フレームをプロセッサーに渡す前に、フレームのディープ コピーも行います。)

期待される結果:

Processor1 は常にメッセージを出力している必要があります。Processor2 は 3 秒ごとにメッセージを出力する必要があります。

問題:

両方のプロセッサが同時に (3 秒ごとに) メッセージを出力します。Processor1 は、メッセージを出力する前に、Processor2 が完了するまで待機します。したがって、出力は次のようになります。

"Message from processor1"
"Message from processor2"
"Message from processor1"
"Message from processor2"
"Message from processor1"
"Message from processor2"

等々。

ここでアイデアが不足しているので、どんな助けでも大歓迎です!

編集: コードの一部を次に示します。

main.cpp:

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    DataSourceThread dataSourceThread;
    dataSourceThread.start();

    GUIThread *guiProcessor = new GUIThread();
    FrameReceiver *guiReceiver = new FrameReceiver(guiProcessor, 0);

    QObject::connect(
        &dataSourceThread, SIGNAL(frameReceived(Frame*)),
        guiReceiver, SLOT(receive(Frame*)),
        Qt::DirectConnection
    );

    DetectorThread *detectorProcessor = new DetectorThread();
    FrameReceiver *detectorReceiver = new FrameReceiver(detectorProcessor, 0);

    QObject::connect(
        &dataSourceThread, SIGNAL(frameReceived(Frame*)),
        detectorReceiver, SLOT(receive(Frame*)),
        Qt::DirectConnection
    );

    return app.exec();
}  

DataSourceThread.cpp から:

void DataSourceThread::run()
{
    ... stuff ...

    while (true) {
        image = cvQueryFrame(capture);

        if (!image) { 
            qDebug() << QString("Could not capture frame"); 
            continue;
        }

        cvReleaseImage(&temp_image);
        temp_image = cvCreateImage(cvSize(640, 480), image->depth, 3);

        cvResize(image, temp_image, 1);

        frame->lock();
        frame->setImage(temp_image);
        frame->unlock();

        emit frameReceived(frame);

        msleep(1); 
    }
} 

FrameReceiver.cpp:

FrameReceiver::FrameReceiver(FrameProcessor* processor, QObject *parent) : QThread(parent) {
    m_ready = true;

    m_processor = processor;
    m_processor->start();

    QObject::connect(
        (QObject*)this, SIGNAL(frameReceived(Frame*)),
        m_processor, SLOT(receive(Frame*)), 
        Qt::QueuedConnection
    );

    QObject::connect(
        m_processor, SIGNAL(ready()),
        (QObject*)this, SLOT(processCompleted()),
        Qt::DirectConnection
    ); }

void FrameReceiver::processCompleted() {
    m_ready = true; }

void FrameReceiver::receive(Frame *frame) {
    if (m_ready == true) {
        m_ready = false;
        frame->lock();
        Frame *f = new Frame(*frame);
        frame->unlock();
        emit frameReceived(f);
    } else {
        // SKIPPED THIS FRAME
    }
}

GUIThread.cpp: (プロセッサ 1)

GUIThread::GUIThread(QObject *parent) : FrameProcessor(parent)
{
    m_frame = new Frame();
}

void GUIThread::setFrame(Frame *frame)
{ 
    qDebug() << QString("Guithread received frame");
}    

FrameProcessor.cpp

// (The processors extend this class)
void FrameProcessor::receive(Frame *frame)
 {
     setFrame(frame);
     delete frame;
     emit ready();
 }

DetectorThread (Processor2)は guithread と同じことを行いますが、setFrame で 3 秒間スリープします。

4

1 に答える 1

3

問題の一部は、すべての QObjects がメイン アプリケーション スレッドによって所有されていることだと思います。これは、非同期シグナルを配信するための 1 つのイベント ループをすべて共有し、処理チェーン全体を効果的にシリアル化することを意味します。

これを設定する適切な方法は次のようになると思います。

GUIProcessor *guiProcessor = new GUIProcessor();
QThread guiProcessorThread;
guiProcessor.moveToThread(&guiProcessorThread);

FrameReceiver *guiReceiver = new FrameReceiver(guiProcessor, 0);
QThread guiReceiverThread;
guiReceiver.moveToThread(&guiReceiverThread);

guiProcessorThread.start();
guiReceiverThread.start();

このようにする場合はDirectConnection、スレッド間で使用しないことBlockingQueuedConnectionをお勧めしますが、次のフレームをキャプチャする前に現在のフレームが確実に処理されるようにする場合に使用します。

これを参照してください:http://labs.qt.nokia.com/2010/06/17/youre-doing-it-wrong/

そしてこれ: http://labs.qt.nokia.com/2006/12/04/threading-without-the-headache/

お役に立てれば!

編集:明確にするために、私の提案では、クラスは QThread の代わりに QObject を継承します。

于 2011-01-18T22:41:32.727 に答える