Why QThread::wait() never returns after invoked QThread::quit()?
-
I have a problem, that
QThread
never returns fromwait()
.
The scenario contains 2 additional threads (QThread
andstd::thread
) besides the main execution thread. Let's call theQThread Q
, thestd::thread
T
and the main threadM
.
InM
I createQ
, the Receiver-objectR
"living" inQ
and the Sender-objectS
. Also astd::thread T
is created executing a bunch if emits withS
.class Sender : public QObject { Q_OBJECT; public: std::vector<int> m_Sent; Sender() { } public slots: signals: void signal(int i); public: void send(int i) { m_Sent.emplace_back(i); emit signal(i); } }; class Receiver : public QObject { Q_OBJECT; public: std::vector<int> m_Received; Receiver() { } void Connect(Sender* s) { connect(s, &Sender::signal, this, &Receiver::slot, Qt::QueuedConnection); } void Disconnect(Sender* s) { disconnect(s, &Sender::signal, this, &Receiver::slot); } public slots: void slot(int i) { m_Received.emplace_back(i); } }; void main(int argc, char** argv) { QApplication app(argc, argv); qint64 random_seed = QDateTime::currentMSecsSinceEpoch(); std::cout << "Setting random seed " << random_seed << "\n"; std::srand(random_seed); std::unique_ptr<Receiver> R(new Receiver); std::unique_ptr<Sender> S(new Sender); auto actions = [&S]() { int i = 0; std::chrono::steady_clock::time_point current = std::chrono::steady_clock::now(); std::chrono::steady_clock::time_point finish = current + std::chrono::milliseconds(100); while (current < finish) { std::this_thread::sleep_for(std::chrono::microseconds(std::rand()%1000)); S->send(i++); std::this_thread::sleep_for(std::chrono::microseconds(std::rand()%1000)); S->send(i++); std::this_thread::sleep_for(std::chrono::microseconds(std::rand()%1000)); S->send(i++); std::this_thread::sleep_for(std::chrono::microseconds(std::rand()%1000)); S->send(i++); std::this_thread::sleep_until(current + std::chrono::milliseconds(17)); current = std::chrono::steady_clock::now(); } }; std::unique_ptr<QThread> Q(new QThread()); R->moveToThread(Q.get()); R->Connect(S.get()); Q->start(); std::thread T(actions); T.join(); QMetaObject::invokeMethod(Q.get(), "quit", Qt::QueuedConnection); Q->wait();// waits forever std::cout << "Sent: "; for(auto v : S->m_Sent) { std::cout << v << " "; } std::cout << std::endl; std::cout << "Received: "; for(auto v : R->m_Received) { std::cout << v << " "; } std::cout << std::endl; }
I'm working on Windows with VS2013 and Qt 5.5.1. While debugging I went through all emits so all should be inserted to event loop in
Q
. Also I tried to use aBlockingQueuedConnection
for the invokedquit()
and removed thewait()
but then I got a Qt-warning/error of a detected Deadlock, but I don't use any locks, so seems to be some Qt-internal stuff I have no insight so far. Thanks for some enlightment on the problem ;-) -
Sweet Mary ... that's one big pile of a mess ... I had to read it 3 times to get what's happening.
Here:QMetaObject::invokeMethod(Q.get(), "quit", Qt::QueuedConnection);
This posts an event to the
QThread
's event loop (which is the main event loop), so the event is never processed as you're blocking the main event loop by callingQThread::wait()
.Q.get()->quit();
should work just fine.
-
There is no main event loop and that is intended. There is only one event loop in the thread handled by
QThread Q
. I tried to call justQ.get()->quit()
but than it happens that not all sent events are really handled. I need a way to put thequit()
at the end of the event loop inQ
and afterwards waiting in the main thread thatQ
is finished. But thanks for your response. -
Still no solution? Doesn't Qt provide a way to insert kind of quit statement in the eventloop of the thread (not the main eventloop).
-
There is no main event loop and that is intended.
Sure there is, as long as you have a
QApplication
object, you have a main event loop, you just don't spin it.Still no solution? Doesn't Qt provide a way to insert kind of quit statement in the eventloop of the thread (not the main eventloop).
Not really, no, and there's no need too. You have that problem because of your mixing of event-driven and iteration based threads, but you can easily roll your own solution. Example follows:
class Sender : public QObject { // ... signals: void signal(int i); void finished(); // ... };
int main(int argc, char** argv) { // ... auto actions = [&S]() { // .... S->finished(); }; std::unique_ptr<QThread> Q(new QThread()); QObject::connect(S.get(), &Sender::finished, R.get(), [&Q] () { Q.get()->quit(); }); // ... T.join(); Q->wait(); // ... return 0; }
-
Yeah right, the main loop exists but is not running, that's what I meant but didn't write it correctly. The instantiation of QApplication was needed ( why ever?) to get a event loop running for the QThread, but I don't need to execute the main loop.
Thanks for your solution, that works not exactly in my case, because I can't change theSender
object (API is fixed). But it can easily be adjust, because the source of thefinished
signal can be anyQObject
, e.g.Receiver
itself or do I run in another pitfall with that approach?//... QObject::connect(R.get(), &Receiver::finished, R.get(), [&Q] () { Q->quit(); }); //... T.join(); emit R->finished(); Q->wait(); //...
Am I lost if I can't change
Sender
andReceiver
types? Meaning is the only solution always to have someQObject
connected to a lambda callingQ->quit()
in context ofR
?
Again thanks for your help, enlightend my understanding of how Qt works inside a little bit. As you may notice I'm testing how Qt works in context outside the Qt universe. -
@Feuerteufel said in Why QThread::wait() never returns after invoked QThread::quit()?:
The instantiation of QApplication was needed ( why ever?)
Because Qt is a complex system, and although you don't see it, underneath there are a zillion global variables that need to be initialized. So
QApplication
should be the firstQObject
that comes into existence (if you rely on anythingQObject
related that is).But it can easily be adjust, because the source of the finished signal can be any QObject, e.g. Receiver itself or do I run in another pitfall with that approach?
No, the receiver can emit the signal, provided it knows when the sequence of w/e tasks is finished. Most of the time this isn't true however, so that is the "pitfall". I find your direct call to a signal a bit dubious though. It isn't wrong (syntactically), but the point of signals is they belong to an object, which object itself signals the world about things that happened ...
Am I lost if I can't change Sender and Receiver types?
Is deriving from them not an option?
Meaning is the only solution always to have some QObject connected to a lambda calling Q->quit() in context of R?
Well, I'd put it in a private slot, but technicalities aside, mostly, yes.
-
Ok, thanks, I think I have all information I need now.