プライム・カウンター

並行処理の進行状況を監視する方法を示します。

次の例は、QFutureWatcher クラスとQt ConcurrentfilteredReduced関数を使用して、インタラクティブでノンブロッキングな QtWidgets アプリケーションを作成する方法を示しています。この例では、QList の整数を作成することができます。リストは1からnまでの自然数で自動的に埋められます。プログラムはリスト内の素数をチェックし、見つかった素数の総数を表示します。

例の実行

Qt Creator からサンプルを実行するには、Welcome モードを開き、Examples からサンプルを選択します。詳細については、Building and Running an Example を参照してください。

接続の設定

Qt Concurrentライブラリは、2 つのモードで動作するfilteredReduced関数を提供します:OrderedReduce and UnorderedReduceOrderedReduce モードでは、リデュース関数は元のシーケンスの順序で呼び出され、UnorderedReduce モードでは、要素はランダムにアクセスされます。

必要な要素でUIを構成した後、QtSignals & Slotsメカニズムを使用して、並行処理のシグナルに接続する必要があります。この例では、QFutureWatcher クラスを使用して並行処理の進行状況を監視し、対話型 GUI の実装に必要なシグナルを提供します。

    ...
connect(ui->pushButton, &QPushButton::clicked,
        this, [this] { start(); });
connect(&watcher, &QFutureWatcher<Element>::finished,
        this, [this] { finish(); });
connect(&watcher, &QFutureWatcher<Element>::progressRangeChanged,
        ui->progressBar, &QProgressBar::setRange);
connect(&watcher, &QFutureWatcher<Element>::progressValueChanged,
        ui->progressBar, &QProgressBar::setValue);
    ...

QFutureWatcher クラスは、並行処理の変更に応じて UI を更新するために必要なシグナルを提供するため、この例では重要な役割を果たします。

並行処理の開始

すべてのシグナルとスロットを接続した後、ユーザーがQPushButtonを押すと、start() 関数が呼び出されます。

start() 、Qt ConcurrentのfilteredReduced関数を呼び出し、QFutureWatcher メンバにfutureを設定します。この処理を確実に並行して実行するために、QThreadPool を最初のパラメータとして指定します。この方法によって、グローバル・スレッド・プールでのブロッキングも回避できます。コンテナとして整数のQList 、静的フィルタとreduce関数、そして最後にReduceOption フラグを渡す。

    ...
void PrimeCounter::start()
{
    if (ui->pushButton->isChecked()) {
        ui->comboBox->setEnabled(false);
        ui->pushButton->setText(tr("Cancel"));
        ui->labelResult->setText(tr("Calculating ..."));
        ui->labelFilter->setText(tr("Selected Reduce Option: %1").arg(ui->comboBox->currentText()));
        fillElementList(ui->horizontalSlider->value() * stepSize);

        timer.start();
        watcher.setFuture(
            QtConcurrent::filteredReduced(
                &pool,
                elementList,
                filterFunction,
                reduceFunction,
                currentReduceOpt | QtConcurrent::SequentialReduce));
    ...

filter関数とreduce関数を見てみよう。これらの関数はメンバ変数に依存しないため、この例では静的と宣言されています。しかし、これらはラムダやメンバ関数として簡単に指定できます。

filter関数は、reduce関数で削減する要素をマークします。この実装は単純な素数フィルターです。この関数は引数としてconst参照を取るので、操作対象のコンテナに対してスレッドセーフな操作が可能です。

    ...
bool PrimeCounter::filterFunction(const Element &element)
{
    // Filter for primes
    if (element <= 1)
        return false;
    for (Element i = 2; i*i <= element; ++i) {
        if (element % i == 0)
            return false;
    }
    return true;
}
    ...

reduce 関数は、その最初のパラメータとして、操作対象のコンテナと同じ型の変更可能な参照を取ります。2番目のパラメータは、filter関数で以前にフィルタリングされた要素です。この例では、素数の数を数えます。

    ...
void PrimeCounter::reduceFunction(Element &out, const Element &value)
{
    // Count the amount of primes.
    Q_UNUSED(value);
    ++out;
}
    ...

プロジェクト例 @ code.qt.io

©2024 The Qt Company Ltd. 本書に含まれる文書の著作権は、それぞれの所有者に帰属します。 本書で提供されるドキュメントは、Free Software Foundation が発行したGNU Free Documentation License version 1.3に基づいてライセンスされています。 Qtおよびそれぞれのロゴは、フィンランドおよびその他の国におけるThe Qt Company Ltd.の 商標です。その他すべての商標は、それぞれの所有者に帰属します。