C++ Qt5:如何在线程中等待信号?

C++ Qt5:如何在线程中等待信号?,c++,multithreading,qt,timer,wait,C++,Multithreading,Qt,Timer,Wait,标题问题可能不是很明确。我正在Windows7上使用Qt5 在某个线程(QThread)的某个点上,在“process()”函数/方法中,我必须等待属于我在此线程中使用的QSslSocket的“encrypted()”信号。另外,我想我应该使用QTimer并等待一个“timeout()”信号,以避免在无限循环中被阻塞… 我现在得到的是: // start processing data void Worker::process() { status = 0; connect(ss

标题问题可能不是很明确。我正在Windows7上使用Qt5

在某个线程(QThread)的某个点上,在
“process()”
函数/方法中,我必须等待属于我在此线程中使用的QSslSocket的
“encrypted()”
信号。另外,我想我应该使用QTimer并等待一个
“timeout()”
信号,以避免在无限循环中被阻塞…
我现在得到的是:

// 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;
}
嗯,很明显,它不起作用。它永远停留在那个while循环中…

所以,问题是:有没有办法解决这个问题?我如何等待
“encrypted()”
信号,但不超过10秒,以避免陷入等待循环/线程

您可以使用本地事件循环来等待信号发出:

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");

在这里,它等待直到发出
加密的
或超时达到。

在异步编程中,“等待”被视为反模式。与其等待,不如设计代码,以便对满足的条件做出反应。例如,将代码连接到信号

实现这一点的一种方法是将您的操作分为不同的状态,并在输入每个状态时执行一些工作。当然,如果工作量非常大,则使用单独的插槽而不是lambda来保持可读性

请注意,没有显式内存管理。使用Qt类的指针是一种过早的优化,应该避免在不必要的地方使用。对象可以是
工作者
(或其成员)的直接成员

子对象必须都是所有权层次结构的一部分,该所有权层次结构在根目录下具有
Worker
。这样,您就可以安全地将
Worker
实例移动到另一个线程,它使用的对象将跟随它。当然,您也可以在正确的线程中实例化
工作者
——有一个简单的方法。线程的事件调度器拥有工作线程,因此当线程的事件循环退出时(即调用
QThread::quit()
),工作线程将被自动释放,不会泄漏任何资源

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);
  });
}
然后:

void waitForEventDispatcher(QThread*thread){
同时(线程->isRunning()&&!线程->事件调度程序())
QThread::yieldCurrentThread();
}
int main(int argc,字符**argv){
QCoreApplication{argc,argv};
结构:QThread{~Thread(){quit();wait();}Thread;
thread.start();
waitForEventDispatcher(线程和线程);
实例化Inthread(&myThread);
...
返回app.exec();
}

请注意,连接到
QThread::started()
将是快速的:只有在
QThread::run()中的某些代码存在时,事件调度程序才存在
有机会执行。因此,我们必须等待线程通过让步到达那里-这很可能让工作线程在一两个让步内取得足够的进展。因此不会浪费太多时间。

这些天我有一些时间,我做了一些调查…
嗯,我浏览过“http://doc.qt.io/qt-5/qsslsocket.html“并发现:

bool QSslSocket::waitForEncrypted(int msecs = 30000)
让我感到羞愧的是,我以前没有注意到……:(
肯定需要买一些眼镜(不幸的是,这不是一个玩笑!)
我愿意相应地修改我的代码以进行测试(周一@office)。
这很有可能奏效。
[后期编辑:
是的,这是我在最后一段代码中实现的解决方案,效果很好,所以我决定分享:)

从Qt 5.0开始,提供了一个等待方法。你将它连接到一个信号,等待()将阻塞,直到信号触发

QSignalSpy spy(SIGNAL(encrypted()));
spy.wait(5000);  //wait until signal fires or 5 second timeout expires

非常好的主意。我测试了它(做了一些修改以适应我的代码和目的),它工作正常。我当然投票赞成:)简单简洁。太棒了。以这种方式使用事件循环将导致“忙等待”,对吗?@harihardik No.在等待主线程中的所有事件都被处理,GUI工作正常。但是这种编码不是很好的做法,建议使用异步方法。@您只需将
加密的
信号与参数连接到lambda,并在lambda中调用事件循环的
退出
方法即可(对事件循环对象的引用应该在lambda中捕获)。非常有趣的方法!我明天将测试它(在调整它以适合我的代码之后),我将尝试提供相关的反馈:)嗯,在线程中有一个状态机真是太好了!抱歉,我太热情了,但我是Qt的新手,所以每次我都会看到一些新的东西(对我来说)我意识到Qt/C++能提供多么巨大的可能性…!我希望我能看到更多这样的例子!我能说什么?太好了,好主意!你能再对此进行一点扩展吗:“注意没有显式内存管理。使用指向Qt类的指针是一种过早的优化,应该避免在不必要的地方使用。”@Mitch Manual memory management(米奇手动内存管理)是指您
新建
某些内容,并将其分配给原始C指针,然后必须手动删除它。该代码甚至没有使用
新建
进行显式内存分配,但如果它有,结果将立即分配给智能指针或在
QObject
树。请注意,
QObject
充当其他
QObject
s的智能指针集合。Mitch许多糟糕的Qt示例,包括与Qt捆绑的示例,都使用完全不必要的显式堆分配。由于每个重要的Qt类都分配一个,因此通过将相当于PIMPL的数量放入PIMPL,分配数量将增加一倍堆上的指针。
QObject
QWidget
的指针大小为~4-6个,即使它们的PIMPL大两个数量级。如果在构造函数中添加子对象,只需将它们作为常规(非指针)添加即可我不确定你是指基于
QObject
的内存管理还是智能指针。
bool QSslSocket::waitForEncrypted(int msecs = 30000)
QSignalSpy spy(SIGNAL(encrypted()));
spy.wait(5000);  //wait until signal fires or 5 second timeout expires