Managing threads in Qt
I was writing a little post about performing a remote power reset, but it is not quite there yet, so I’ll share with you some of the ideas about multithreading in UI applications. Let’s say we’re developing a simple client-server application that is required to use a callback function every time there is new data available. Therefore, we need to listen the port for the incoming data constantly, which is impossible in the main thread. Here comes the multithreading: we have to spawn a listener thread, while our main thread is waiting for the instructions.
There are two ways to create a parallel application in C++: thread-based and task-based. std::thread
vs std::async
. I personally prefer the std::thread
way, simply because std::async need the std::launch::async
policy specified for the true asynchronous calculations.
For example, let’s use a class:
class Foo {
private:
std::thread RxThread;
//…
void InfiniteRead(std::function<void(uint8_t*, size_t &)> callback) {
for (;;) {
//Read Some Data
if(read) {
callback(data, size_of_data);
}
}
}
//…
public:
void Init() {
RxThread = std::thread(&Foo::InfiniteRead, this, callback);
}
};
Voila! We’ve spawned a thread, which constantly reads data and calls some function on received data. Problems start when we need to update the UI according to the received data. It is a bad habit to change anything in the UI from other threads, because you never know if it’s being updated from the main thread right now.
One should use events in Qt to update the UI from (sort of) another thread. Firstly you should declare a class, which would handle the events:
class MyEvent : public QEvent{
public:
struct event_msg{
//some custom struct with data
};
MyEvent(const event_msg& message) : QEvent(QEvent::User) {_message = message;}
~MyEvent() {}
event_msg message() const {
return _message;
}
private:
event_msg _message;
};
Then you declare a simple method in the UI header file:
bool event(QEvent* event);
With the following implementation:
bool UI_Class::event(QEvent* event){
if (event->type() == QEvent::User){
MyEvent* postedEvent = static_cast<MyEvent*>(event);
//some code
}
return QWidget::event(event);
}
In the overridden event method, we check the event type, and if it is the one we’ve made, we execute some commands. Otherwise, we send the event further down the food chain. And the last thing is how to send such events:
void blabla(){
...
MyEvent::event_msg event;
//fill event_msg with data
MyEvent* e = new MyEvent(event);
QCoreApplication::postEvent(parent, e);
}
Done. As simple as that. Also, it’s very good idea to notify the thread via the std::condition_variable
, that you’ve received some data.