8

私が遭遇した問題はQThreads、多数の記事に基づいて、想定されている方法で実装することにしたことです:
https://www.qt.io/blog/2010/06/17/youre-doing-it-wrong
http:/ /mayaposch.wordpress.com/2011/11/01/how-to-really-truly-use-qthreads-the-full-explanation/

手元の問題は、アルゴリズムが別々に実行されるためですQObject(ラップされていQThreadます)。Thread::Sleepどうすれば、またはsmthのようなものを呼び出すことができますか..何かアイデアはありますか?

ソフトウェアの簡単な説明。基本的に、私のアプリケーションはTSP(巡回セールスマンの問題) を解決します。検索が進むにつれて、履歴のすべての状態がframes.. (ビジュアル フレームのように) として保存されます。検索アルゴリズムは 1 つのスレッドで実行されます。メインスレッドは GUI で処理しています。次に、どのフレームを画面に表示するかMediaplayerをスレッドに伝える like スレッドがあります。Mainでは、睡眠はどこから来るのでしょうか? GUI には、ユーザーが早送りしたり、通常のペースで移動したりするために使用できるスライダーがあります。そのスライダーは、信号スロットを介してMediaplayerスレッドに高速または低速で移動するよう指示します。

4

4 に答える 4

7

私たちが行ったことは、基本的に次のようなものです: (このコンピューターでコードをチェックアウトしていないため、記憶によって書かれています)

class Sleeper : public QThread {
public:
   void sleep(int ms) { QThread::sleep(ms); }
};

void sleep(int ms);

// in a .cpp file:
static Sleeper slp;

void sleep(int ms) {
    slp.sleep(ms);
}

重要なのは、インスタンスQThread::sleepによって表されるスレッドではなく、関数が呼び出し元のスレッドをスリープ状態にすることです。したがって、カスタムサブクラスQThreadを介してそれを呼び出すラッパーを作成するだけです。QThread

残念ながら、 QThread はめちゃくちゃです。ドキュメントには、誤って使用するように記載されています。あなたが見つけたように、いくつかのブログ投稿はそれを行うためのより良い方法を教えてくれますが、そもそも保護されたスレッドメンバーであってはならないsleepのような関数を呼び出すことはできません。

そして何よりも、QThread をどのように使用しても、これまでに考えられた中でおそらく最悪のスレッド API である Java のものをエミュレートするように設計されています。のようなまともなものと比較すると、boost::thread肥大std::thread化して複雑すぎて不必要に使いづらく、膨大な量のボイラープレート コードが必要になります。

これは、Qt チームが吹き飛ばした場所の 1 つです。ビッグタイム。

于 2012-05-27T19:11:39.717 に答える
4

簡単な答え:非同期の実行から完了までのコードでブロックすることは想定されていません。aのすべてのイベントハンドラーとスロットの実装は、QObjectその役割を果たし、できるだけ早く戻ることになっています。忙しい待機や睡眠をすることは想定されていません。この線に沿ったより多くの怒りについては、MiroSamekのIhateRTOSesを参照してください

上記に続くはるかに優れた実装については、代わりにこの回答を参照してください。以下に続くマクロのトリックは、Cで立ち往生している貧しい魂に任せるのが最善です。

少なくともコードの機能の観点から、正しい方法でそれを行う方法の例を添付しました。実際の実装が必要な場合は、Boostのスタックレスコルーチンをご覧ください

マクロトリックはシンタックスシュガーです-それはテクニックをより口当たりの良いものにします(ブーストは私が以下よりもうまくやってくれます)。マクロを使用するか、メソッドを明示的に書き出すかは、あなた次第です。構文は、それを行うための「正しい方法」であると主張されているものではありません。このようなプリプロセッサのトリックを使用しているのは私だけではありません。欠落しているのは、ネストされた関数呼び出しと、内での実行から完了までの実行の複数の「スレッド」のサポートQObjectです。この例は、1つの「スレッド」と1つのレベルの非同期関数呼び出しのみのコードを示しています。Stackless Pythonは、これを論理的な結論に導きます。

非同期で作成すると、すべてのコードにこのパターンが表示されます。SLEEPマクロは、コードを理解しやすくするためのシンタックスシュガーです。構文が圧倒されないC++のハッキーなマクロなしでそれを書くための本当にきれいな方法はありません。C ++ 11の時点でも、この言語にはyieldのサポートが組み込まれていません。C ++0xにyieldが追加されなかった理由を参照してください。。

これは本当にノンブロッキングコードです。「スリープ」しているときに定期的なタイマーイベントが発生することがわかります。この協調マルチタスクは、OSによって実行されるスレッド/プロセスの切り替えよりもはるかに低いオーバーヘッドであることに注意してください。16ビットのWindowsアプリケーションコードがこのように記述されたのには理由があります。それは、貧弱なハードウェアでも非常にうまく機能します。

このコードはを必要とせQThread、実際にはを使用しないことに注意してくださいQThread。ただし、オブジェクトを優先度の高いスレッドに移動すると、遅延の広がりは小さくなります。

Qtタイマーの実装は、期間が「短い」場合、Windowsのタイマーティック期間を短縮するのに十分賢いです。以下に示すプラットフォーム固有のコードを使用できますが、お勧めできません。Qt 5では、Qt::PreciseTimerタイマーを開始するだけです。Windows 8より前のシステムでは、ここでのパフォーマンスのために、消費電力とわずかに高いカーネルオーバーヘッドをトレードオフしていることに注意してください。Windows 8、OS X(xnu)、および最新のLinuxはティックレスであり、このようなパフォーマンスの低下に悩まされることはありません。

##および__LINE__(ポジショニングマクロとのトークン連結)を使用したCマクロの作成からの明確なプリプロセッサの悪用の方向性を認める必要があります。

マクロと同様に、SLEEP()マクロを実装して、GOTO()わかりやすいブロッキングコードスタイルで記述されているが、バックグラウンドで非同期である単純な有限状態マシンを使用できるようにすることもできます。状態の開始や終了などで実行されるアクションを実装するためのマクロを使用できますがENTER()LEAVE()コードは完全にストレートコード化されたブロッキングスタイルの関数のように見えます。構文上のシュガーコーティングがないコードよりも、非常に生産的で、従うのが簡単であることがわかりました。YMMV。最終的には、UMLステートチャートに向かっているものがありますが、QStateMachine-based実装よりもオーバーヘッド(実行時とコードテキストの両方)が少なくなります。

以下は出力です。アスタリスクは定期的なタイマーティックです。

doing something
*
*
*
*
*
*
*
*
*
*
slept, a=10
*
*
*
*
*
slept, a=20
*
*
slept, a=30
*
slept, a=40
#sleep.pro
QT       += core
QT       -= gui
TARGET = sleep
CONFIG   += console
CONFIG   -= app_bundle
TEMPLATE = app
SOURCES += main.cpp
//main.cpp
#ifdef Q_WS_WIN
#include <windows.h>
#endif
#include <cstdio>
#include <QtCore/QTextStream>
#include <QtCore/QObject>
#include <QtCore/QBasicTimer>
#include <QtCore/QTimer>
#include <QtCore/QCoreApplication>

QTextStream out(stdout);

// this order is important
#define TOKENPASTE2(x,y) x ## y
#define TOKENPASTE(x,y) TOKENPASTE2(x,y)
#define SLEEP(ms) sleep(ms, &SLEEPCLASS::TOKENPASTE(fun, __LINE__)); } void TOKENPASTE(fun, __LINE__)() {

class Object : public QObject
{
    Q_OBJECT
    #define SLEEPCLASS Object // used by the SLEEP macro
public:
    Object() {
        QTimer::singleShot(0, this, SLOT(slot1()));
        periodic.start(100);
        connect(&periodic, SIGNAL(timeout()), SLOT(tick()));
    }
protected slots:
    void slot1() {
        a = 10; // use member variables, not locals
        out << "doing something" << endl;
        sleep(1000, &Object::fun1);
    }
    void tick() {
        out << "*" << endl;
    }

protected:
    void fun1() {
        out << "slept, a=" << a << endl;
        a = 20;
        SLEEP(500);
        out << "slept, a=" << a << endl;
        a = 30;
        SLEEP(250);
        out << "slept, a=" << a << endl;
        a = 40;
        SLEEP(100);
        out << "slept, a=" << a << endl;
        qApp->exit();
    }

private:
    int a; // used in place of automatic variables

private:
    void sleep(int ms, void (Object::*target)()) {
        next = target;
        timer.start(ms, this);
    }
    void timerEvent(QTimerEvent * ev)
    {
        if (ev->timerId() == timer.timerId()) {
            timer.stop(); (this->*next)();
        }
    }
    QTimer periodic;
    QBasicTimer timer;
    void (Object::* next)();
};

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    Object o1;
#ifdef Q_WS_WIN
   timeBeginPeriod(1); // timers will be accurate to 1ms
#endif
    return a.exec();
}

#include "main.moc"
于 2012-05-31T23:25:13.270 に答える