C++ Qt5:如何在线程中等待信号?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/31358646/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Qt5: How to wait for a signal in a thread?
提问by ????? ?????
Probably the title question is not very explicit. I am using Qt5 on Windows7.
可能标题问题不是很明确。我在 Windows7 上使用 Qt5。
In a thread (QThread) at some point, in the "process()"
function/method, I must wait for the "encrypted()"
SIGNAL belonging to a QSslSocket I am using in this thread. Also I suppose I should use a QTimer and wait for a "timeout()"
SIGNAL in order to avoid getting blocked in an infinite loop...
What I have now is:
在某个线程 (QThread) 中,在"process()"
函数/方法中,我必须等待"encrypted()"
属于我在该线程中使用的 QSslSocket的SIGNAL。另外我想我应该使用 QTimer 并等待"timeout()"
SIGNAL 以避免在无限循环中被阻塞......
我现在拥有的是:
// start processing data
void Worker::process()
{
status = 0;
connect(sslSocket, SIGNAL(encrypted()), this, SLOT(encryptionStarted()));
QTimer timer;
connect(&timer, SIGNAL(timeout()), this, SLOT(timerTimeout()));
timer.start(10000);
while(status == 0)
{
QThread::msleep(5);
}
qDebug("Ok, exited loop!");
// other_things here
// .................
// end other_things
emit finished();
}
// slot (for timer)
void Worker::timerTimeout()
{
status = 1;
}
// slot (for SSL socket encryption ready)
void Worker::encryptionStarted()
{
status = 2;
}
Well, obviously it doesn't work. It stays in that while-loop forever...
So, the question is: Is there a way to solve this problem? How can I wait for that "encrypted()"
SIGNAL but not more than - let's say 10 seconds - in order to avoid getting stuck in that waiting-loop/thread?
好吧,显然它不起作用。它永远停留在那个while循环中......
所以,问题是:有没有办法解决这个问题?我怎样才能等待该"encrypted()"
信号但不超过 - 假设 10 秒 - 以避免陷入该等待循环/线程?
回答by Nejat
You can use a local event loop to wait for the signal to be emitted :
您可以使用本地事件循环来等待发出信号:
QTimer timer;
timer.setSingleShot(true);
QEventLoop loop;
connect( sslSocket, &QSslSocket::encrypted, &loop, &QEventLoop::quit );
connect( &timer, &QTimer::timeout, &loop, &QEventLoop::quit );
timer.start(msTimeout);
loop.exec();
if(timer.isActive())
qDebug("encrypted");
else
qDebug("timeout");
Here it waits until encrypted
is emitted or the the timeout reaches.
在这里它等待直到encrypted
发出或超时到达。
回答by Reinstate Monica
In asynchronous programming, the "wait for" is considered an anti-pattern. Instead of waiting for things, design the code to react to a condition becoming fulfilled. E.g., connect the code to a signal.
在异步编程中,“等待”被认为是一种反模式。与其等待事情发生,不如设计代码以对满足的条件做出反应。例如,将代码连接到信号。
One way of implementing this is to slice your actions into separate states, and do some work when each of the states is entered. Of course if the amount of work is non-trivial, use a separate slot instead of a lambda to keep things readable.
实现这一点的一种方法是将您的操作分成不同的状态,并在进入每个状态时做一些工作。当然,如果工作量很大,请使用单独的插槽而不是 lambda 来保持可读性。
Note the absence of explicit memory management. Use of owning pointers to Qt classes is a premature optimization and should be avoided where unnecessary. The objects can be direct members of the Worker
(or its PIMPL).
注意没有显式的内存管理。使用拥有指向 Qt 类的指针是一种过早的优化,应该避免在不必要的情况下。对象可以是Worker
(或其PIMPL)的直接成员。
The sub-objects must be all a part of the ownership hierarchy that has Worker
at the root. That way, you can safely move the Worker
instance to another thread, and the objects it uses will follow it. Of course you could also instantiate the Worker
in the correct thread - there's a simple idiomfor that. The thread's event dispatcher owns the worker, thus when the thread's event loop quits (i.e. after invoking QThread::quit()
), the worker will be automatically disposed and no resources will leak.
子对象必须全部是具有Worker
根的所有权层次结构的一部分。这样,您可以安全地将Worker
实例移动到另一个线程,并且它使用的对象将跟随它。当然,你也可以Worker
在正确的线程中实例化- 有一个简单的习惯用法。线程的事件调度器拥有worker,因此当线程的事件循环退出时(即调用后QThread::quit()
),worker 将被自动处理,不会泄漏资源。
template <typename Obj>
void instantiateInThread(QThread * thread) {
Q_ASSERT(thread);
QObject * dispatcher = thread->eventDispatcher();
Q_ASSERT(dispatcher); // the thread must have an event loop
QTimer::singleShot(0, dispatcher, [dispatcher](){
// this happens in the given thread
new Obj(dispatcher);
});
}
The Worker's implementation:
Worker 的实现:
class Worker : public QObject {
Q_OBJECT
QSslSocket sslSocket;
QTimer timer;
QStateMachine machine;
QState s1, s2, s3;
Q_SIGNAL void finished();
public:
explicit Worker(QObject * parent = {}) : QObject(parent),
sslSocket(this), timer(this), machine(this),
s1(&machine), s2(&machine), s3(&machine) {
timer.setSingleShot(true);
s1.addTransition(&sslSocket, SIGNAL(encrypted()), &s2);
s1.addTransition(&timer, SIGNAL(timeout()), &s3);
connect(&s1, &QState::entered, [this]{
// connect the socket here
...
timer.start(10000);
});
connect(&s2, &QState::entered, [this]{
// other_things here
...
// end other_things
emit finished();
});
machine.setInitialState(&s1);
machine.start();
}
};
Then:
然后:
void waitForEventDispatcher(QThread * thread) {
while (thread->isRunning() && !thread->eventDispatcher())
QThread::yieldCurrentThread();
}
int main(int argc, char ** argv) {
QCoreApplication app{argc, argv};
struct _ : QThread { ~Thread() { quit(); wait(); } thread;
thread.start();
waitForEventDispatcher(&thread);
instantiateInThread<Worker>(&myThread);
...
return app.exec();
}
Note that connecting to QThread::started()
would be racy: the event dispatcher doesn't exist until some code within QThread::run()
had a chance to execute. Thus we have to wait for the thread to get there by yielding - this is very likely to get the worker thread to progress far enough within one or two yields. Thus it won't waste much time.
请注意,连接到QThread::started()
会很活跃:直到其中的某些代码QThread::run()
有机会执行时,事件调度程序才存在。因此,我们必须等待线程通过让步到达那里 - 这很可能使工作线程在一两个产量内取得足够的进展。因此不会浪费太多时间。
回答by ????? ?????
I had some time these days and I did some investigation...
Well, I browsed "http://doc.qt.io/qt-5/qsslsocket.html" and found this:
这些天我有一些时间,我做了一些调查......
好吧,我浏览了“ http://doc.qt.io/qt-5/qsslsocket.html”并发现了这个:
bool QSslSocket::waitForEncrypted(int msecs = 30000)
To my real shame, I didn't noticed it before... :(
Definitely need to buy some glasses (unfortunately, it's not a joke!)
I am willing to modify my code accordingly in order to test it (on Monday @ office).
Pretty much chances that it'll work.
Yes, kinda weird to answer my own question, but maybe it IS a solution, so I decided to share :)
令我感到遗憾的是,我之前没有注意到它...... :(
肯定需要买一些眼镜(不幸的是,这不是开玩笑!)
我愿意相应地修改我的代码以测试它(星期一@办公室)
它很有可能会起作用。
是的,回答我自己的问题有点奇怪,但也许这是一个解决方案,所以我决定分享:)