繁体   English   中英

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

[英]Qt5: How to wait for a signal in a thread?

可能标题问题不是很明确。 我在 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");

在这里它等待直到encrypted被发出或超时到达。

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

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

注意没有显式的内存管理。 使用拥有指向 Qt 类的指针是一种过早的优化,应该避免在不必要的情况下。 对象可以是Worker (或其PIMPL )的直接成员。

子对象必须都是以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);
  });
}

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();
  }
};

然后:

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();
}

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

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

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

这些天我有一些时间,我做了一些调查......
好吧,我浏览了“http://doc.qt.io/qt-5/qsslsocket.html”,发现了这个:

bool QSslSocket::waitForEncrypted(int msecs = 30000)

令我真正感到羞耻的是,我之前没有注意到它...... :(
肯定需要买一些眼镜(不幸的是,这不是开玩笑!)
我愿意相应地修改我的代码以对其进行测试(星期一@办公室)。
很有可能它会起作用。
[后期编辑]:
是的,这是我在最终代码中实现的解决方案,效果很好,所以我决定分享:)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM