Hi all,
Background
I’ve been thinking about Qt’s multithreading API. Currently, Qt’s suggestions for multithreading are:
Subclass a worker QObject and move it to a QThread (if an event loop is desired)
Subclass QThread^ (if an event loop is not desired AND recycling QThreads is not desired)
Subclass QRunnable and attach it to a QThreadPool (if an event loop is not desired AND recycling QThreads is desired)
Call QtConcurrent::run() (if a parallel function call is desired AND recycling QThreads is desired)
Use QtConcurrent’s filter-map-reduce API (if high-level container processing is desired)
(^The Qt Project community is divided on this; some discourage all subclassing of QThread, but Qt’s maintainers say it’s valid in some cases)
Current shortcomings:
The API is quite disparate; the code for the different approaches don’t look like they come from the same library. Furthermore, some features are missing:
The ability to do a parallel function call, or use QRunnable, without being tied to a thread pool
The ability to emit a signal when a thread finishes a QRunnable
The ability to delay a parallel function call
The ability to elegantly separate code control and thread logic (hence the long-running “Subclass QThread or not?” debate)
Proposal
So, here are some proposed additions to the QThread and QThreadPool classes, to make the API it more unified and flexible:
class QThread
{
...
public:
/*1*/ static QThread* setupSimpleThread(QRunnable* runnable);
/*2*/ static QThread* setupSimpleThread(Function func, ...);
/*3*/ static QThread* setupEventLoop(QObject* worker);
};
class QThreadPool
{
...
public:
/*4*/ bool QThreadPool::tryStart(Function func, ...);
bool QThreadPool::tryStart(QRunnable* runnable); // Already exists
};
Behaviour:
(1) binds a QRunnable to a QThread, which will call the QRunnable::run() when start()‘ed.
(2) binds a function and zero or more arguments to a QThread, which will call the function when start()‘ed.
(3) moves the worker QObject to the new thread, ready to have its slots invoked when the QThread is start()‘ed.
(4) is similar to (2), but it uses a recyclable thread from a QThreadPool and (tries to) start immediately
Advantages:
The missing features mentioned earlier are provided
A symmetrical API for using both recycled and unrecycled threads — QThreadPool and QThread
A unified API, which clearly distinguishes and enforces the 2 different approaches to using QThread — both with and without an event loop
A clean separation between thread control (QThread) and threaded code (QRunnable), thus making it more idiot-proof. Also achieved without the huge overhead of the worker-object approach
Demos
Want to use signal-slot connections across threads? Set up a parallel event loop:
class WorkerObject : public QObject {...};
WorkerObject *worker = new WorkerObject();
QThread *thread = QThread::setupEventLoop(worker);
connect(...)
thread->start();
Don’t want to subclass QThread, but don’t want to put up with the code+runtime overhead of a worker QObject either? Subclass QRunnable instead:
class MyRunnable : public QRunnable {...};
QThread *thread = QThread::setupSimpleThread(new MyRunnable(...));
connect(thread, SIGNAL(finished()), ...); // end-of-QRunnable signal!
thread->start();
Want to run a “single-shot” function in a separate thread? Take your pick:
void myFunc(int arg1, float arg2) {...}
QThread *thread = QThread::setupSimpleThread(&myFunc, 123, 45.6);
connect(thread, SIGNAL(finished()), ...);
thread->start();
// OR, to recycle threads,
QThreadPool::globalInstance()->tryStart(&myFunc, 123, 45.6);
Request for feedback
What do you think? Is this a worthwhile addition to Qt? Is there anything missing?
↧