3

いくつかの計算集約型タスク (マシン ビジョン) 用の C++ クラス ライブラリを開発しています。

// I am a part of a Qt-agnostic library
class Cruncher
{
    /* ... */
public:
    void doStuff();
};

次に、そのライブラリを使用する Qt GUI があります。ライブラリから負荷の高いルーチンを呼び出すワーカー スレッドを作成しています。

// I am a part of a Qt-based GUI which utilizes the library
class Worker : public QThread
{
    /* ... */
protected:
    virtual void run()
    {
        /* ... */
        Cruncher c;
        for (int i = 0; i < count; ++i)
            c.doStuff(); // takes some time, and while it's working 
                         // it should communicate status changes which should
                         // become visible in the GUI
    }
};

doStuff() の内部では多くのことが起こり、doStuff() が戻るのを待たずに、何が起こっているかについてユーザーにフィードバックを提供したいと考えています。1 つには、doStuff() を呼び出すたびにメーターを 1 ステップずつ増やすだけでなく、細かい進捗レポートを作成できる可能性があります。また、doStuff() は、作業の一部を続行できる重大ではない障害に遭遇する可能性がありますが、Cruncher が動作している (および Worker が現在 への呼び出しでビジー状態である) ときに、これが発生したときに GUI にメッセージを表示したいと考えています。 doStuff()))。

ライブラリを Qt に依存しないままにしたいので、Cruncher にシグナルとスロットを追加する気はありません。Qtクラスではない場合にGUIにフィードバックを提供してその作業を報告できるようにする他の方法はありますか?

Worker の実行中に一定の間隔で Cruncher の「status」および「errorMsg」メンバーをポーリングする QTimer を作成することを検討していましたが、これは非常に最適ではないようです。

4

3 に答える 3

4

@Nimのアドバイスを受けましたが、回答をもう少し冗長にして、誰かが同じ問題を抱えている場合に役立つようにしたいので、私は自分の回答を投稿しています。

ライブラリにメッセージ ディスパッチャのスケルトンを作成しました。

// doesn't need to know about Qt
class MessagePort
{
public:
    virtual void message(std::string msg) = 0;
};

次に、このオブジェクトへのハンドルを Cruncher に追加し、時折 message() を呼び出して doStuff() にスパイスを加えました。

// now with Super Cow powers!
class Cruncher
{
protected:
    MessagePort *msgPort_;

public:
    Cruncher(MessagePort *msgPort) : msgPort_(msgPort) {}
    void doStuff()
    {
        while(...)
        {
            /*...*/
            msgPort_->message("Foo caused an overload in Bar!");
        }
    }
};

最後に、必要なすべての Qt の利点を使用して、GUI 内に MessagePort の実装を作成しました。

class CruncherMsgCallback : public QObject, public MessagePort
{
    Q_OBJECT

public:
    CruncherMsgCallback() : QObject(), MessagePort()
    {
        connect(this, SIGNAL(messageSignal(const QString &)), 
                GUI,    SLOT(messageShow(const QString &)), 
                Qt::QueuedConnection);
    }

    virtual void message(std::string msg)
    {
        emit messageSignal(QString::fromStdString(msg));
    }

signals:
    void messageSignal(const QString &msg);
};

最後に、Worker が Cruncher のインスタンスを作成すると、動作中の MessagePort へのポインターも与えられます。

class Worker
{
protected:
    virtual void run()
    {
        CruncherMsgCallback msgC;
        Cruncher c(&msgC); // &msgC works as a pointer to a 
                           // generic MessagePort by upcasting
        c.doStuff(); // Cruncher can send messages to the GUI 
                     // from inside doStuff()
    }
};
于 2013-02-07T19:13:34.430 に答える
1

メソッドからシグナルを安全に送信できrun()ます。ワーカー スレッドからメイン スレッドに情報を渡すには、これが最善の方法だと思います。シグナルを QThread サブクラスに追加するだけです (QThread スレッドがどのように機能するかがまったくわからない場合は、スロットを追加しないでください)。

問題を回避するために、これらのシグナルからの接続を明示的にキューに入れることをお勧めします。デフォルトでは、自動接続タイプも機能して Queued シグナルを発行するはずですが、このような場合は明示的に指定した方がよいと思います。実際には直接シグナルもそのように機能するはずですが、Qt に処理させるのではなく、スレッドセーフを自分で処理する必要があり、メインでのみ機能する QtGui クラスのいずれかを使用するスロットに接続することはできません。スレッドなので、キューに入れられた接続に固執することをお勧めします。

簡単な情報をrun()メソッドに渡すために、すぐに反応する必要がない場合は、いくつかの共有QAtomicInt変数またはそのようなものをフラグとして使用すると、ワーカー スレッドが都合のよいときにチェックします。ポーリングが必要なもう少し複雑な方法は、ミューテックスで保護する共有データ構造を持つことです。その方向に通信するより複雑な方法は、ある種のメッセージキューを必要とします (その方向に信号を送信するときに、Qt がメインスレッドのイベントループで使用するのと同じように)。

于 2013-02-07T15:22:19.667 に答える
1

コールバック関数(クラス)などを利用し、構築時に渡す。報告する必要があるものは、そのコールバックを介して報告します。

于 2013-02-07T15:11:37.783 に答える