我偶然发现了这个问题,正如其他人所做的那样: QThread won't stop / does not process a signal QThread - Using a slot quit() to exit the thread
问题是我想要一个工作线程启动,做一些工作(包括在我的代码中向其他线程发送信号,以及异步接收信号)然后退出。但我希望这个线程与启动它的代码同步。换句话说,我希望在创建工作线程的代码中执行,直到工作线程完成其工作。
但似乎这在Qt中是不可能的。原因是工作者的QThread.quit()槽不能在线程本身内发出信号。侦听此插槽信号的事件循环应驻留在创建工作线程的同一线程中。这意味着不应该阻止创建线程,否则工作线程永远不会停止。
这让我想到了QThread.wait()的重点是什么呢?我认为这个函数应该只是停留在程序的末尾以确保所有线程都已退出,但它实际上不能用于同步线程,至少它不能用于同步工作线程与创建的线程它。因为如果从创建线程调用QThread.wait(),它会阻止其事件循环,这将阻止工作线程的接口,这将阻止它退出。
我错过了什么吗?
我认为我需要添加一个代码段:
for (auto i = myVector.begin(); i < myVector.end(); ++i)
{
// 5-line best practice creation for the thread
QThread* workerThread = new QThread;
MyWorkerObject* workerObject = new MyWorkerObject(0);
workerObject->moveToThread(workerThread);
QObject::connect(workerThread, SIGNAL(started()), workerObject, SLOT(init()));
QObject::connect(workerThread, SIGNAL(finished()), workerObject, SLOT(deleteLater()));
// Stop mechanism
QObject::connect(workerObject, SIGNAL(finished()), workerThread, SLOT(quit()));
// Start mechanism
wokerThread->start();
// Invoking the work
QMetaObject::invokeMethod(workerObject, "StartYourJob", Qt::QueuedConnection, Q_ARG(SomeType, *i));
// Synchronization
workerThread->wait();
delete wokerThread;
}
答案 0 :(得分:2)
我终于在这里找到了答案: http://comments.gmane.org/gmane.comp.lib.qt.user/6090
简而言之,如果将QThread :: quit()作为一个槽调用,那么创建线程的事件循环处理程序将处理它,这不是我想要的。
我应该直接打电话。因此,当workerObject完成其工作时,它不应发送信号(必须通过阻塞的创建线程),而应直接调用其容器的退出:
this->thread()->quit();
这将是workerObject的退出点。现在不需要停止机制,可以从代码中删除这些行。
// Stop mechanism
QObject::connect(workerObject, SIGNAL(finished()), workerThread, SLOT(quit()));
有人看到这种方法有什么问题吗?
答案 1 :(得分:1)
线程的目的是允许进程同时运行(同时!),所以如果你只是创建一个线程来做工作并等待当前线程,你就不需要使用新线程。
为了回答你关于QThread :: wait()的目的的问题,Qt文档声明它类似于POSIX函数pthread_join。快速搜索pthread_join会显示this link,其中说明理由如下: -
pthread_join()函数是一种已被证明有用的便利 多线程应用程序。程序员确实可以 如果没有通过传递额外状态提供,则模拟此函数 作为start_routine()的参数的一部分。终止线程 将设置一个标志以指示终止并广播条件 那是该州的一部分;一个加入的线程会等待 条件变量。虽然这种技术允许线程 等待更复杂的条件(例如,等待多个 线程终止),等待单个线程终止 被认为广泛有用。另外,包括pthread_join()函数 绝不排除程序员编写这种复杂的等待。从而, 虽然不是原始的,包括本卷中的pthread_join() POSIX.1-2008被认为是有价值的。
pthread_join()函数提供了一个允许的简单机制 应用程序等待线程终止。线程之后 终止后,应用程序可以选择清理资源 被线程使用了。例如,在pthread_join()返回后, 可以回收任何应用程序提供的堆栈存储。
pthread_join()或pthread_detach()函数最终应该是 调用使用detachstate属性创建的每个线程 设置为PTHREAD_CREATE_JOINABLE,以便与存储关联 线程可以回收。
pthread_join()和取消之间的交互是 由于以下原因而定义明确:
pthread_join()函数,与所有其他非async-cancel-safe一样 函数只能用延迟可取消性类型调用。
在禁用的可取消状态下不能进行取消。
因此,只需要考虑默认的可取消状态。如 指定,pthread_join()调用被取消,或者成功, 但不是两个。从那以后,应用程序的区别是显而易见的 要么运行取消处理程序,要么返回pthread_join()。那里 因为在延迟中调用了pthread_join(),所以没有竞争条件 可取消状态。
如果实现检测到该线程指定的值 pthread_join()的参数不引用可连接的线程,它是 建议函数失败并报告[EINVAL] 错误。
如果实现检测到该线程指定的值 pthread_join()的参数是指调用线程,它是 建议该功能失败并报告[EDEADLK] 错误。
如果某个实现在其结束后检测到使用了线程ID 生命周期,建议该功能失败并报告 [ESRCH]错误。
答案 2 :(得分:0)
QThread :: wait()不是你需要的。这个函数正是你提到的,它等待线程终止。
bool QThread :: wait(unsigned long time = ULONG_MAX)
阻止线程,直到满足以下任一条件:
与此QThread对象关联的线程已完成执行(即何时执行) 从run()返回。如果线程已完成,此函数将返回true。它也是 如果线程尚未启动,则返回true。
时间毫秒已过。如果时间是ULONG_MAX(默认值),那么等待将是 永远不会超时(线程必须从run()返回)。如果是,此函数将返回false 等待超时。
如果你需要同步两个线程(你的主线程和创建的线程),那么我建议使用信号和插槽来指示哪一个准备就绪(触发isReady bool)并进行while (!isReady) { sleep(1ms); processEvents(); }
循环。可能不是最好的方法,但应该有效。