If you review the docs of the Worker class we see that it indicates how to establish the additional arguments:
class Worker(QRunnable): "" " Worker thread Inherits from QRunnable to handler worker thread setup, signals and wrap - up. : param callback: The function callback to run on this worker thread.Supplied args and kwargs will be passed through to the runner.: type callback: function: param args: Arguments to pass to the callback function: param kwargs: Keywords to pass to the callback function #...
In your case you must change to:
worker = Worker(self.execute_this_fn, arg1, arg2)
On the other hand, if the following part of the source code is observed:
#... self.kwargs["progress_callback"] = self.signals.progress #...
To use one of the QThreadPool threads, subclass QRunnable and implement the run() virtual function. Then create an object of that class and pass it to start() .,If no threads are available at the time of calling, then this function does nothing and returns false . Otherwise, runnable is run immediately using one available thread and this function returns true .,This function will always increase the number of active threads. This means that by using this function, it is possible for activeThreadCount() to return a value greater than maxThreadCount() .,Note that QThreadPool is a low-level class for managing threads, see the Qt Concurrent module for higher level alternatives.
class HelloWorldTask(QRunnable):
def run(self):
print "Hello world from thread", QThread.currentThread()
hello = HelloWorldTask()
# QThreadPool takes ownership and deletes 'hello'
automatically
QThreadPool.globalInstance().start(hello)
C++ developers strive to build robust multithreaded applications, but multithreading was never an easy thing to do. In this article, Toptal Freelance Qt Developer Andrei Smirnov talks about several scenarios exploring concurrent programming with the Qt framework. , suspend() would be a blocking call that sets the thread waiting on a waiting condition. This would be done by posting a suspend request into the queue and waiting until it is handled. Pretty much similar to QThread::quit() + wait().,Multithreading is a programming and execution model that allows multiple threads to exist within the context of a single process. These threads share the process' resources but are able to execute independently.,You can also run a QThread without any event loop by overriding QThread::run() method and this is perfectly fine as long as you know what you are doing. For example, do not expect method quit() to work in such case.
The great flexibility of the Qt framework allows you to work around the “missing event loop” problem and to add one to QRunnable
:
class MyTask: public QObject, public QRunnable {
Q_OBJECT
public:
void MyTask::run() {
_loop.exec();
}
public slots:
// you need a signal connected to this slot to exit the loop,
// otherwise the thread running the loop would remain blocked...
void finishTask() {
_loop.exit();
}
private:
QEventLoop _loop;
}
A naïve solution to this problem could be using a QMutex
. Inside the task function, you could simply acquire the mutex effectively serializing all threads attempting to run the task. This would guarantee that only one thread at a time could be running the function. However, this solution impacts the performance by introducing high contention problem because all those threads would be blocked (on the mutex) before they can proceed. If you have many threads actively using such a task and doing some useful job in between, then all these threads will be just sleeping most of the time.
void logEvent(const QString & event) {
static QMutex lock;
QMutexLocker locker( & lock); // high contention!
logStream << event; // exclusive resource
}
The first option is to use QThreadPool
. We can create a QThreadPool
instance and use QThreadPool::setMaxThreadCount(1)
. Then we can be using QtConcurrent::run()
to schedule requests:
class Logger: public QObject {
public: explicit Logger(QObject * parent = nullptr): QObject(parent) {
threadPool.setMaxThreadCount(1);
}
void logEvent(const QString & event) {
QtConcurrent::run( & threadPool, [this, event] {
logEventCore(event);
});
}
private: void logEventCore(const QString & event) {
logStream << event;
}
QThreadPool threadPool;
};
Implementation of LogWorker
constructor and logEvent
is straightforward and therefore not provided here. Now we need a service that will be managing the thread and the worker instance:
// interface
class LogService: public QObject {
Q_OBJECT
public:
explicit LogService(QObject * parent = nullptr);
~LogService();
signals:
// to use the service, just call this signal to send a request:
// logService->logEvent("event");
void logEvent(const QString & event);
private:
QThread * thread;
LogWorker * worker;
};
// implementation
LogService::LogService(QObject * parent): QObject(parent) {
thread = new QThread(this);
worker = new LogWorker;
worker - > moveToThread(thread);
connect(this, & LogService::logEvent, worker, & LogWorker::logEvent);
connect(thread, & QThread::finished, worker, & QObject::deleteLater);
thread - > start();
}
LogService::~LogService() {
thread - > quit();
thread - > wait();
}
If your worker is dealing with timers or sockets, you need to ensure the destructor is executed in the same thread (the thread you created for the worker and where you moved the worker to). The obvious way to support this is to subclass QThread
and delete
worker inside QThread::run()
method. Consider the following template:
template <typename TWorker>
class Thread : QThread
{
public:
explicit Thread(TWorker *worker, QObject *parent = nullptr)
: QThread(parent), _worker(worker) {
_worker->moveToThread(this);
start();
}
~Thread() {
quit();
wait();
}
TWorker worker() const {
return _worker;
}
protected:
void run() override {
QThread::run();
delete _worker;
}
private:
TWorker *_worker;
};