QThread&C ++ 11 lambda:等待完成

Hhu*_*hut 2 lambda qt qthread c++11

我希望从lambda异步执行代码,同时保持eventloop的运行,我想:这可以工作...

auto thread = QSharedPointer<QThread>(new QThread);

QEventLoop l;
connect( thread.data(), &QThread::finished, &l, &QEventLoop::quit );
connect( thread.data(), &QThread::started, [=]() {
    for(int i=0; i<100; ++i ) {
        qDebug() << "waiting... " << i;
    }
    QThread::currentThread()->sleep(10);
} );
thread->start();
l.exec();
auto const fin = thread->wait();
qWarning() << fin;
Run Code Online (Sandbox Code Playgroud)

一切都按预期工作,但是:当线程完成其lambda函数时,我没有得到任何帮助。它似乎finished没有发出,并且wait(即使没有额外的事件循环)也将永远阻塞。

如何使事件循环退出或等待返回?还是有更好的方法让lambda在另一个线程中运行,并通过非阻塞事件循环等待它?

谢谢

Rei*_*ica 5

有两个问题:

  1. connectQThread::started对拉姆达缺乏给定线程的对象上下文。因此,lambda将在当前线程中执行(特别是in thread->thread(),与thread!不相同)。

  2. 您永远不会quit线程。

对象上下文与线程不同。您需要一个QObject存在于给定线程中的;它不能是线程本身。A QThread实际上是一个线程句柄,并不意味着要驻留在其自己的线程中(而且不是!)。抵制将QThread实例移至其线程的冲动:然后,您将很难过地看到一个句柄位于它管理的线程中。线程结束后,句柄由于移到空线程而变得不起作用。

旁注:

  1. 除非您确实需要共享线程,否则只需在本地分配它即可。尝试最小化代码中显式内存管理的数量。
  2. QThread::sleep 是静态的。
  3. QEventLoop::quit即使没有记录,也是线程安全的。您可以退出lambda,保存一个连接。
QThread thread;
QEventLoop loop;
QObject context;
context.moveToThread(&thread);
connect(&thread, &QThread::started, &context, [&]() {
    qDebug() << "waiting... ";
    QThread::sleep(10);
    qDebug() << "done";
    loop.quit();
});
thread.start();
loop.exec();
thread.quit();
thread.wait();
// some other code
Run Code Online (Sandbox Code Playgroud)

las,这导致重新输入事件循环和意大利面条代码:世界是异步的。无法保证不会从事件循环中重新输入运行所有这些方法。相反,您应该将控制权返回给基本事件循环。您还应该利用线程池,以免手动管理线程。创建线程是昂贵的,瞬态线程是反模式和过早的悲观化。当然,也许您的共享线程本来应该被重用,但是即使那样,您也很可能未充分利用它。全局线程池实例具有对整个应用程序需求的全局洞察力,并且可以更好地管理线程生存期。

void doFirst() {
  QtConcurrent::run([this]{
    qDebug() << "waiting...";
    QThread::sleep(10);
    qDebug() << "done";
    QObject src;
    src.connect(&src, &QObject::destroyed, this, [this]{ doNext(); });
    // see /sf/ask/1515252721/ for better ways
    // of invoking doNext
  });
} 

void doNext() {
  // some other code
}
Run Code Online (Sandbox Code Playgroud)

有关在给定线程/对象上下文中执行代码的更好方法,请参见此问题

如果您的lambda受I / O约束,则应为其使用一个自定义的较大线程池(且仅针对它们)。QtConcurrent::run可以将您的线程池作为Qt 5.4以来的第一个参数。