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

时间:2016-08-04 14:09:57

标签: qt c++11 lambda qthread

我希望在保持eventloop运行的同时异步执行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在另一个线程中运行并使用非阻塞事件循环等待它?

谢谢

2 个答案:

答案 0 :(得分:5)

有两个问题:

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

  2. 你永远不会quit线程。

  3. 对象上下文与线程不同。你需要一个存在于给定线程中的QObject;它不能成为线程本身。 QThread实际上是一个线程句柄,并不意味着生活在自己的线程中(并且它没有!)。抵制将QThread实例移动到其线程的冲动:然后你有一个令人遗憾的句柄,它位于它管理的线程中。一旦线程结束,句柄就会变为无效,因为它会移动到空线程。

    附注:

    1. 除非你真的需要共享线程,否则只需在本地分配即可。尽量减少代码中显式内存管理的数量。
    2. QThread::sleep是静态的。
    3. QEventLoop::quit是线程安全的,即使没有记录如此。您可以退出lambda,保存一个连接。
    4. 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
      

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

      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绑定的,你应该为它们使用一个自定义的,更大的线程池(并且只为它们使用)。 QtConcurrent::run可以将您的线程池作为自Qt 5.4以来的第一个参数。

答案 1 :(得分:-1)

当我使用QThread时,我总是使用this作为提醒。

在链接提供的示例中,作者使工作人员生成finished信号。

这里是代码:

QThread* thread = new QThread;
Worker* worker = new Worker();
worker->moveToThread(thread);
connect(worker, SIGNAL(error(QString)), this, SLOT(errorString(QString)));
connect(thread, SIGNAL(started()), worker, SLOT(process()));
connect(worker, SIGNAL(finished()), thread, SLOT(quit()));
connect(worker, SIGNAL(finished()), worker, SLOT(deleteLater()));
connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
thread->start();

工作人员是执行真实代码的人。

我知道这不是你的用例(lambda无法发出这样的信号),只是在思考。