Super easy C++ multi-threaded applications with Qt 6 and Concurrent

Bryan Cairns (VoidRealms)
3 min readJul 14, 2021

--

Threading is powerful, but also as a developer its insanely complex and will drive you absolutely insane…

How do start a thread?

How do you get results back?

How can you re-use threads?

Let’s take a simple example, you want to take a simple function, have it run in a different thread and get the results back.

The application conceptually might look like…

Our application starts with the main function which will obviously run any code we want.

But at some point we have a need to get a value, however getting that value might take some time, and we want to continue on with our main function without blocking.

This is where threads would work perfectly, but of course now we have a lot of extra complexity.

Eventually we need the value returned from the thread.

Any seasoned developer will tell you this simple diagram is really not that simple when you dive into the details of how to make this application. Threads are also expensive, what if we need to get this value multiple times?

This is where Qt really can help!

In my Qt 6Core Advanced Course on Udemy, I deep dive into this subject, but let’s take a look at how we can use Qt to solve this issue — this may look confusing but don’t worry Qt makes the code much easier…

At a high level, we will:

Run our main function, at some point we will want a value, but we want to move that function generating the value to another thread, and when we are ready we will get the value back from the function in the future.

Source code: Available on GitHub

Step 1 — Add the Qt Concurrent module to CMake

cmake_minimum_required(VERSION 3.14)project(qt6ca-3-20 LANGUAGES CXX)set(CMAKE_INCLUDE_CURRENT_DIR ON)set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
find_package(QT NAMES Qt6 Qt5 COMPONENTS Core Concurrent REQUIRED)
find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core Concurrent REQUIRED)
add_executable(qt6ca-3-20
main.cpp
)
target_link_libraries(qt6ca-3-20 Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Concurrent)

Step 2 — Write the code

#include <QCoreApplication>
#include <QDebug>
#include <QFuture>
#include <QFutureWatcher>
#include <QList>
#include <QtConcurrent>
int multiply(const int &value)
{
qInfo() << "Called: " << value;
int num = value * 10;
return num;
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QList<int> list;
for (int i = 0;i < 100; i++)
{
list.append(i);
}
QFutureWatcher<int> watcher;
QFuture<int> future = QtConcurrent::mapped(list,&multiply);
watcher.setFuture(future);

//Set signals and slots here
qInfo() << "Back in main!"; //Do other things with the watcher if needed
watcher.waitForFinished();
qInfo() << "Done!";
qInfo() << future.results();
return a.exec();
}

The basics of this are…

Make a QFutureWatcher, this will monitor the QFuture (a value we get in the future) for completion.

QFutureWatcher<int> watcher;

Then we make a call to Qt concurrent and tell the watcher to monitor it:

QFuture<int> future = QtConcurrent::mapped(list,&multiply);
watcher.setFuture(future);

Qt Concurrent does a lot of work in the background, basically it grabs a thread from the applications thread pool, and moves the call to the multiply function to an available thread. The function runs on the thread, and when the function returns, QFutureWatcher knows when the work is complete.

When we run out of things to do on our main thread, we can block the application, until the value is ready, this step is optional, as the QFuture::results() will also block:

//Do other things with the watcher if needed
watcher.waitForFinished();
...
qInfo() << future.results();

See this is easy!

--

--

Bryan Cairns (VoidRealms)
Bryan Cairns (VoidRealms)

Written by Bryan Cairns (VoidRealms)

Computer programmer loves, Qt, Python, Flutter

No responses yet