QThread & C++11 lambda:等待完成
QThread & C++11 lambda: wait for finished
我希望在保持事件循环 运行ning 的同时从 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
(即使没有额外的事件循环)将永远阻塞。
如何让事件循环退出或等待 return?或者这有更好的方法让 lambda 运行 在另一个线程中并使用非阻塞事件循环等待它?
谢谢
当我使用 QThread 时,我总是使用 this 作为提醒。
在link提供的例子中,作者让worker产生了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 不能发出这样的信号),它只是在思考。
有两个问题:
从 QThread::started
到 lambda 的 connect
缺少给定线程的对象上下文。因此,lambda 将在当前线程中执行(特别是在 thread->thread()
中,这与 thread
不同!)。
你永远不会quit
线程。
对象上下文与线程不同。您需要一个存在于给定线程中的 QObject
;它不可能是线程本身。 QThread
实际上是一个线程句柄,并不意味着存在于它自己的线程中(事实并非如此!)。抵制将 QThread
实例移动到它的线程的冲动:然后你会遇到一个不幸的情况,一个句柄存在于它管理的线程中。线程一结束,句柄就变为非功能,因为它移动到空线程。
旁注:
- 除非确实需要共享线程,否则直接在本地分配即可。尽量减少代码中显式内存管理的数量。
QThread::sleep
是静态的。
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
唉,这导致重新进入事件循环和意大利面条代码:世界是异步的。无法保证您 运行 所有这些的方法不会从事件循环中重新进入。相反,您应该 return 控制基本事件循环。您还应该利用线程池,以免手动管理您的线程。线程的创建是昂贵的,瞬态线程是一种反模式和过早的悲观情绪。当然,也许您的共享线程本应被重用,但即便如此您也很可能未充分利用它。全局线程池实例对整个应用程序的需求具有全局洞察力,可以更好地管理线程生命周期。
void doFirst() {
QtConcurrent::run([this]{
qDebug() << "waiting...";
QThread::sleep(10);
qDebug() << "done";
QObject src;
src.connect(&src, &QObject::destroyed, this, [this]{ doNext(); });
// see for better ways
// of invoking doNext
});
}
void doNext() {
// some other code
}
有关在给定 thread/object 上下文中执行代码的更好方法,请参阅 this question。
如果您的 lambda 是 I/O 绑定的,您应该为它们(并且只为它们)使用一个自定义的、更大的线程池。 QtConcurrent::run
自Qt 5.4起可以将你的线程池作为第一个参数。
我希望在保持事件循环 运行ning 的同时从 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
(即使没有额外的事件循环)将永远阻塞。
如何让事件循环退出或等待 return?或者这有更好的方法让 lambda 运行 在另一个线程中并使用非阻塞事件循环等待它?
谢谢
当我使用 QThread 时,我总是使用 this 作为提醒。
在link提供的例子中,作者让worker产生了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 不能发出这样的信号),它只是在思考。
有两个问题:
从
QThread::started
到 lambda 的connect
缺少给定线程的对象上下文。因此,lambda 将在当前线程中执行(特别是在thread->thread()
中,这与thread
不同!)。你永远不会
quit
线程。
对象上下文与线程不同。您需要一个存在于给定线程中的 QObject
;它不可能是线程本身。 QThread
实际上是一个线程句柄,并不意味着存在于它自己的线程中(事实并非如此!)。抵制将 QThread
实例移动到它的线程的冲动:然后你会遇到一个不幸的情况,一个句柄存在于它管理的线程中。线程一结束,句柄就变为非功能,因为它移动到空线程。
旁注:
- 除非确实需要共享线程,否则直接在本地分配即可。尽量减少代码中显式内存管理的数量。
QThread::sleep
是静态的。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
唉,这导致重新进入事件循环和意大利面条代码:世界是异步的。无法保证您 运行 所有这些的方法不会从事件循环中重新进入。相反,您应该 return 控制基本事件循环。您还应该利用线程池,以免手动管理您的线程。线程的创建是昂贵的,瞬态线程是一种反模式和过早的悲观情绪。当然,也许您的共享线程本应被重用,但即便如此您也很可能未充分利用它。全局线程池实例对整个应用程序的需求具有全局洞察力,可以更好地管理线程生命周期。
void doFirst() {
QtConcurrent::run([this]{
qDebug() << "waiting...";
QThread::sleep(10);
qDebug() << "done";
QObject src;
src.connect(&src, &QObject::destroyed, this, [this]{ doNext(); });
// see for better ways
// of invoking doNext
});
}
void doNext() {
// some other code
}
有关在给定 thread/object 上下文中执行代码的更好方法,请参阅 this question。
如果您的 lambda 是 I/O 绑定的,您应该为它们(并且只为它们)使用一个自定义的、更大的线程池。 QtConcurrent::run
自Qt 5.4起可以将你的线程池作为第一个参数。