BackgroundRunner
% Remco Bloemen % 2014-06-12
Usage
The usage is much like
QtConcurrent::run
except you return what I call a Joiner; a function that integrates the
result back into the calling thread:
BackgroundRunner::run([=]() {
// <Background task>
return [=]() {
// <Joiner>
};
});
Lambda’s returning lambda’s; welcome to the world of higher-order programming!
The Background task will be run a background thread using
QtConcurrent::run
. You have full access to the variables from the
enclosing scope, notable the this
pointer, but very aware of thread
safety, because everything will be run in parallel!
When it is finished it returns the Joiner, which is queued for the
event loop of the calling thread. Again you have access to the this
pointer, but this time we are back in the objects own thread, it is as
safe to use the this
pointer as in any slot
.
Below is an real-world example of function in a QAbstractItemModel
that uses the BackgroundRunner
to calculate UI content on demand,
without blocking the UI.
QString DataModel::message(uint rowId)
{
if(cache.contains(rowId))
return cache[rowId];
// Get the row data to calculate the message
// Let's do it here to avoid concurrency issues
const RowData rowData = dataForRow(rowId)
// Calculate the message in the background
BackgroundRunner::run([=]() {
// Code here will be run in a background thread.
// We can now use rowData, it will be a copy of the one defined on line 8
const QString message = expensiveCalculateMessage(rowData);
// Add the messge to the cache and inform the model
return [=]() {
// Code here can safely access both (copies of) the result from the
// background task, and the DataModel object. It can even emit signals!
_cache[rowId] = message;
emit refresh(rowId);
};
});
// Show ellipsis while we are waiting for the message to be done.
return "…";
}