2

私は、ラインスキャン カメラと対話するアプリケーションを構築することで、自分の道を歩もうとしています。最終的に、 (データ取得) から(データ処理)に 100 ミリ秒unsigned shortごとに 384x128 値の「ブロック」(つまり、配列) を渡したいと考えています。これは、次のブロックが到着する前にデータを処理するのに 100 ミリ秒かかることを意味します。QThreadQRunnableQRunnable

データを移動する正しい方法はまだわかりません。現在、私は QVector を使用しています。Qt4では、オブジェクトが書き込まれるまで、シグナルで発行された場合、QVectorがコピーされないことを意味する暗黙の共有を理解しています。しかし、私が作成した小さなテスト アプリケーションでは、それが何を意味するのか正確に理解できていません。以下に提供されるMWEの出力を次に示します...

Acquire thread: init.
Acquire thread: block acquired: 0x106485e78 Content: {1, 2, 3, 4}
GUI thread: received signal: 0x7fff5fbfda98 Content: {1, 2, 3, 4}
Acquire thread: block acquired: 0x106485e78 Content: {1, 2, 3, 4}
GUI thread: received signal: 0x7fff5fbfda98 Content: {1, 2, 3, 4}
Acquire thread: block acquired: 0x106485e78 Content: {1, 2, 3, 4}
GUI thread: received signal: 0x7fff5fbfda98 Content: {1, 2, 3, 4}

4 つの値を持つ「ダミー」の QVector を使用し、スレッドの実行時にベクトルのアドレスを追跡しています。データは全体的に正しいですが、コピーが作成されているようです。アプリケーションのどの時点でもデータを変更していません...表示するだけです。const QVector<unsigned short>全体を通して、参照などを使用するさまざまな反復を使用してみました。アドレスは常に変更されます。ここではパフォーマンスが重要になるため、384*128 の値の場合に QVector のコピーを作成することに関心があります。

また、別の SO の質問では、QRunnable にデータを受信させる方法を理解するのに苦労しています (この例ではすべて除外されています)。しかし、私の考えは QRunnable が QThread に存在する への参照で動作するようにすることであるため、ここで注意することが重要ですimage_buffer。私はそれを行う方法を理解していません。

具体的な質問:

-- コピーが作成されたように見えるのはなぜですか? これはマルチスレッドのせいですか?

-- 明示的に参照 (つまり、image_buffer&) を使用して、QVector を完全に削除する必要がありますか? または、コピーについて心配する必要がありますか?

-- 私の状況で QThread から QRunnable にデータを渡す正しい方法は何ですか?

以下の MWE は、さまざまなアドレスを示しています。免責事項: 私は C++ を学んでいます。正式なトレーニングはなく、目の前にいくつかの優れた本があります。

main.cpp

#include <QApplication>
#include <QMetaType>
#include <QVector>

#include "appwidget.h"

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

    QApplication app(argc, argv);

    AppWidget gui;
    gui.show();

    qRegisterMetaType<QVector<unsigned short> >("QVector<unsigned short>");

    return app.exec();
}

** appwidget.h **

#ifndef APPWIDGET_H
#define APPWIDGET_H

#include <QWidget>
#include <QVector>

#include "acquire.h"

class AppWidget : public QWidget
{ Q_OBJECT

 public:
  AppWidget(QWidget *parent = 0);

 protected:
  Acquire thread;

 public slots:
  void processBlock(QVector<unsigned short>);

};

#endif

appwidget.cpp

#include <QtGui>
#include <iostream>

#include "appwidget.h"

AppWidget::AppWidget(QWidget *parent)
    : QWidget(parent)
{

    thread.liftoff();
    connect(&thread, SIGNAL(blockAcquired(QVector<unsigned short>)), this, SLOT(processBlock(QVector<unsigned short>)));

    setWindowTitle(tr("TestApp"));
    resize(550, 400);

}

void AppWidget::processBlock(QVector<unsigned short> display_buffer)
{
    std::cout << "GUI thread: received signal: " 
          << &display_buffer 
          << " Content: {" 
          << display_buffer.at(0) << ", " 
          << display_buffer.at(1) << ", "
          << display_buffer.at(2) << ", "
          << display_buffer.at(3)
          << "}" << std::endl;

}

取得.h

#ifndef ACQUIRE_H
#define ACQUIRE_H

#include <QVector>
#include <QThread>

class Acquire : public QThread {

  Q_OBJECT

 public:
    Acquire(QObject *parent = 0);
    ~Acquire();
    QVector<unsigned short> display_buffer;

    void liftoff();

 signals:
    void blockAcquired(QVector<unsigned short>);

 protected:
    void run();

 private:

};

#endif

取得.cpp

#include <iostream>
#include <time.h>
#include <stdlib.h>

#include "acquire.h"

Acquire::Acquire(QObject *parent)
     : QThread(parent)
{
}

Acquire::~Acquire()
{
    std::cout << "Acquire thread: dying." << std::endl;
    wait();
}

void Acquire::liftoff()
{
    std::cout << "Acquire thread: init." << std::endl;
    start();
}

void Acquire::run()
{
    QVector<unsigned short> display_buffer(2 * 2);

    forever {

    /* 
       display_buffer will ultimate be a memcpy of image_buffer
       .. image_buffer updated continuously, 1 line every 800000ns x 128 lines
    */

    display_buffer[0] = 1;
    display_buffer[1] = 2;
    display_buffer[2] = 3;
    display_buffer[3] = 4;
    nanosleep((struct timespec[]){{0, 800000*128}}, NULL);

    std::cout << "Acquire thread: block acquired: " 
          << &display_buffer 
          << " Content: {" 
          << display_buffer.at(0) << ", " 
          << display_buffer.at(1) << ", "
          << display_buffer.at(2) << ", "
          << display_buffer.at(3)
          << "}" << std::endl;

    emit blockAcquired(display_buffer);

    }
}
4

2 に答える 2

3

この場合、コピーが作成されます。これは、値渡しであることと、スレッド境界を越えたシグナルがキューに入れられていることの両方のためです。ただし、暗黙の共有は浅いコピーであることを意味するため、問題ありません。オリジナルとコピーの両方が読み取りのみに使用される場合、コピーのオーバーヘッドは実質的にありません。

残念ながら、あなたのプログラムでは実際にはそうではありません。永久ループは、信号の放出後にループバックするときにベクトルを変更します。この例では、常に 1,2,3,4 を割り当てるだけなので、実際にはベクトル内の何も変更されませんが、非 const operator[] を呼び出すだけでディープ コピーをトリガーできます。

私の結論は次のとおりです。同期してリーダーとライターの間で同じバッファーを共有するか、非同期で書き込みバッファーのコピーをリーダーに渡すことができます。非同期にすることはできず、リーダーとライターの間で同じバッファーを共有できません。

これを処理している方法は、非同期処理には適しているようです。データ生成とデータ処理の特性によっては、同期ソリューションの方が優れている場合もあれば、そうでない場合もあります。非同期コードを同期させる最も簡単な方法は、接続タイプをQt::BlockingQueuedConnectionon connectとして指定することです。

于 2013-10-01T17:00:01.727 に答える
0

2 番目の質問に答えるには、 (QObject が常にリストの最初にあることを確認しQObjectてください) を使用して多重継承を作成できます。QRunnable次に、テスト例とまったく同じ方法で、シグナル/スロット メカニズムを使用してデータを渡すことができます。

class DataProcessor : public QObject, public QRunnable
{ Q_OBJECT
public:

public slots:
   void processBlock(QVector<unsigned short>);
};

実際にはAcquire、スレッド自体に余分な機能を追加するのではなく、別のスレッドでコードを実行することが目的であるため、この構造をクラスにも使用できます。

于 2013-10-01T18:22:57.410 に答える