コンカレント・タスク

QtConcurrent::task は、タスクを別のスレッドで実行するための代替インターフェースを提供する。関数の戻り値は、 APIを通じて利用できる。QFuture

パラメータを調整せずに関数を別スレッドで実行したい場合は、QtConcurrent::runQtConcurrent::task は、余分な構成ステップを実行する必要がある場合のために設計されています。

この関数はQt Concurrentフレームワークの一部です。

Fluent インターフェース

QtConcurrent::task は、QtConcurrent::QTaskBuilder という補助クラスのインスタンスを返します。通常、このクラスのインスタンスを手動で作成する必要はありません。QtConcurrent::QTaskBuilder は、さまざまなタスク・パラメーターを連鎖的に調整するためのインターフェースを提供します。このアプローチはフルエント・インターフェイスとして知られている。

必要なパラメーターを設定するだけで、タスクをキックオフできる。タスクの設定を確定するには、QtConcurrent::QTaskBuilder::spawn を呼び出す必要がある。この関数はノンブロッキング(即座にfutureオブジェクトを返す)ですが、タスクが即座に開始されることは保証されていません。タスクのステータスをモニターするには、QFutureQFutureWatcher クラスを使うことができます。

以下の例と説明を参照してください。

別のスレッドでタスクを実行する

別のスレッドで関数を実行するには、QtConcurrent::QTaskBuilder::spawn を使用します:

QtConcurrent::task([]{ qDebug("Hello, world!"); }).spawn();

これは、デフォルトのQThreadPool から取得した別のスレッドでラムダ関数を実行します。

タスクに引数を渡す

引数を持つ関数を呼び出すには、引数をQtConcurrent::QTaskBuilder::withArguments に渡します:

auto task = [](const QString &s){ qDebug() << ("Hello, " + s); };
QtConcurrent::task(std::move(task))
    .withArguments("world!")
    .spawn();

QtConcurrent::QTaskBuilder::withArguments が呼び出された時点で各引数のコピーが作成され、これらの値はタスクの実行を開始するときにスレッドに渡される。QtConcurrent::QTaskBuilder::withArguments を呼び出した後に引数に加えられた変更は、スレッドからは見えない。

引数を参照で受け取る関数を実行したい場合は、std::ref/cref補助関数を使うべきである。これらの関数は、渡された引数の周りに薄いラッパーを作ります:

QString s("Hello, ");
QtConcurrent::task([](QString &s){ s.append("world!"); })
    .withArguments(std::ref(s))
    .spawn();

ラップされたすべてのオブジェクトが十分に長生きするようにしてください。タスクがstd::ref/crefでラップされたオブジェクトより長生きすると、未定義の動作になる可能性があります。

タスクから値を返す

QFuture APIを使ってタスクの結果を得ることができる:

auto future = QtConcurrent::task([]{ return 42; }).spawn();
auto result = future.result(); // result == 42

QFuture::result() はブロッキング呼び出しであり、結果が利用可能になるまで待つことに注意。タスクの実行が終了し、結果が利用可能になったときに通知を受け取るには、QFutureWatcher を使用します。

結果を別の非同期タスクに渡したい場合は、QFuture::then() を使用して、依存タスクのチェーンを作成できます。詳細はQFuture のドキュメントを参照してください。

その他のAPI機能

さまざまなタイプの callable オブジェクトの使用

厳密には、以下の条件を満たすタスクと引数であれば、どのようなタイプでも使用できます:

std::is_invocable_v<std::decay_t<Task>, std::decay_t<Args>...>

フリー関数を使用する:

QVariant value(42);
auto result = QtConcurrent::task([](const QVariant &var){return qvariant_cast<int>(var);})
                  .withArguments(value)
                  .spawn()
                  .result(); // result == 42

メンバ関数を使用できます:

QString result("Hello, world!");

QtConcurrent::task(&QString::chop)
    .withArguments(&result, 8)
    .spawn()
    .waitForFinished(); // result == "Hello"

operator()を持つ callable オブジェクトを使用できます:

auto result = QtConcurrent::task(std::plus<int>())
                  .withArguments(40, 2)
                  .spawn()
                  .result() // result == 42

既存の callable オブジェクトを使用したい場合は、QtConcurrent::task にコピー/移動するか、std::ref/cref でラップする必要があります:

struct CallableWithState
{
    void operator()(int newState) { state = newState; }

    // ...
};

// ...

CallableWithState object;

QtConcurrent::task(std::ref(object))
   .withArguments(42)
   .spawn()
   .waitForFinished(); // The object's state is set to 42

カスタムスレッドプールの使用

カスタムのスレッド・プールを指定することができる:

QThreadPool pool;
QtConcurrent::task([]{ return 42; }).onThreadPool(pool).spawn();

タスクの優先度設定

タスクの優先度を設定できる:

QtConcurrent::task([]{ return 42; }).withPriority(10).spawn();

futureオブジェクトが不要な場合は、QtConcurrent::QTaskBuilder::spawn(QtConcurrent::FutureResult::Ignore) を呼び出すことができる:

QtConcurrent::task([]{ qDebug("Hello, world!"); }).spawn(FutureResult::Ignore);

関数内にQPromise<T> & 型の追加引数を定義することで、タスクに関連付けられたプロミス・オブジェクトにアクセスできます。この追加引数は関数に渡される最初の引数でなければならず、プロミスとの同時実行モードと同様に、関数はvoid型を返すことが期待されます。結果報告はQPromise API を通して行われます:

void increment(QPromise<int> &promise, int i)
{
    promise.addResult(i + 1);
}

int result = QtConcurrent::task(&increment).withArguments(10).spawn().result(); // result == 11

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