簡體   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