QThread 和 C++11 lambda : wait for finished

标签 qt c++11 lambda qthread

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

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;

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

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

谢谢

最佳答案

有两个问题:

  1. QThread::started 到 lambda 的 connect 缺少给定线程的对象上下文。因此,lambda 将在当前线程中执行(特别是在 thread->thread() 中,这与 thread 不同!)。

  2. 您永远不会退出线程。

对象上下文与线程不同。您需要一个存在于给定线程中的 QObject;它不可能是线程本身。 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

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

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

void doNext() {
  // some other code
}

有关在给定线程/对象上下文中执行代码的更好方法,请参阅 this question .

如果您的 lambda 是 I/O 绑定(bind)的,您应该为它们(并且只为它们)使用一个自定义的、更大的线程池。自 Qt 5.4 以来,QtConcurrent::run 可以将您的线程池作为第一个参数。

关于QThread 和 C++11 lambda : wait for finished,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38769756/

相关文章:

mysql - Qt isOpen 方法 false 值 MySQL

c++ - Qt中重叠多边形的透明度

c++ - 当函数执行结束时, vector 中的线程会发生什么?

c++ - 可以从 istream_iterator 制作 move_iterator 吗?

linq - Lambda 表达式中的 OrderBy 降序排列?

c++ - 如何自动关闭打开的 vtk 窗口?

c++ - 在系统级别监听关键事件

c++ - 使用对 const char * 的右值引用的重载解析

c# - 从现有表达式创建新表达式

java - 使用 recover() 时类型不匹配错误