Concurrent Task#

task provides an alternative interface for running a task in a separate thread. The return value of the function is made available through the QFuture API.

If you want to just run a function in a separate thread without adjusting any parameters, use run as that lets you write less code. The task is designed for cases where you need to perform extra configurations steps.

This function is a part of the Qt Concurrent framework.

Fluent interface#

The task returns an instance of an auxiliary class called QTaskBuilder . Normally, you don’t need to create an instance of this class manually. The QTaskBuilder provides an interface to adjust different task parameters in a chain-like manner. This approach is known as a fluent interface .

You can just set the parameters you need and then kick a task off. In order to finalize the configuration of a task you must invoke spawn . This function is non-blocking (i.e. returns a future object immediately), but it’s not guaranteed that the task starts immediately. You can use the QFuture and QFutureWatcher classes to monitor the status of the task.

See more examples and explanations below.

Running a task in a separate thread#

To run a function in another thread, use spawn :

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

This will run a lambda function in a separate thread obtained from the default QThreadPool .

Passing arguments to the task#

Invoking a function with arguments is done by passing them to withArguments :

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

A copy of each argument is made at the point where withArguments is called, and these values are passed to the thread when it begins executing the task. Changes made to the arguments after calling withArguments are not visible to the thread.

If you want to run a function that accepts arguments by reference, you should use std::ref/cref auxiliary functions. These functions create thin wrappers around passed arguments:

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

Make sure that all wrapped objects live long enough. It is possible to get undefined behavior if a task outlives the object wrapped by std::ref/cref.

Returning values from the task#

You can obtain the result of a task with the QFuture API:

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

Note that result() is a blocking call, it waits for the result to become available. Use QFutureWatcher to get a notification when the task has finished execution and the result is available.

In case you want to pass a result to another asynchronous task, you can use then() to create a chain of dependent tasks. See the QFuture documentation for more details.

Additional API features#

Using different types of callable objects#

Strictly speaking, you can use any type of tasks and arguments that satisfy the following condition:

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

You can use a free function:

value = QVariant(42)
result = QtConcurrent.task(qvariant_cast<int>)
                  .withArguments(value)
                  .spawn()
                  .result() # result == 42

You can use a member function:

result = QString("Hello, world!")
QtConcurrent.task(QString.chop)
    .withArguments(result, 8)
    .spawn()
    .waitForFinished() # result == "Hello"

You can use a callable object with an operator():

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

If you want to use an existing callable object, you need to either copy/move it to task or wrap it with std::ref/cref:

class CallableWithState():

    def operator(newState): state = newState
    # ...

# ...
object = CallableWithState()
QtConcurrent.task(std.ref(object))
   .withArguments(42)
   .spawn()
   .waitForFinished() # The object's state is set to 42

Using custom thread pool#

You can specify a custom thread pool:

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

Setting priority for a task#

You can set the priority for a task:

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

If you don’t need a future object, you can call spawn ( Ignore ):

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

You can access the promise object associated with the task by defining an additional argument of QPromise<T> & type inside the function. This additional argument must be the first argument passed to the function, and like in Concurrent Run With Promise mode, the function is expected to return void type. Result reporting is done through QPromise API:

def increment(promise, i):

    promise.addResult(i + 1)

result = QtConcurrent.task(increment).withArguments(10).spawn().result() # result == 11()