【问题标题】:What is the use of QThread.wait() function?QThread.wait() 函数有什么用?
【发布时间】:2013-11-20 05:27:25
【问题描述】:

我偶然发现了这个问题,就像其他人一样: 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(),它会阻塞它的事件循环,这将阻塞工作线程的接口,这将阻止它退出。

我错过了什么吗?

我想我需要添加一个代码sn-p:

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;
}

【问题讨论】:

  • I want the execution in the code which creates the worker thread to be halted until the worker thread is done its job. 看来你根本不需要线程。
  • Alex,正如我所提到的,worker 的工作“涉及在我的代码中向其他线程发送信号,并异步接收信号”。如果我根本不创建线程,就不会有事件循环来监听信号......
  • QThread::wait 需要进行清理。它一直等到超时或线程完成。你不应该删除正在运行的线程的对象,因为它会导致意想不到的错误,所以在删除之前你应该调用wait(someInterval)
  • 这就是我正在做的事情 Marek。请看我添加的代码 sn-p...

标签: c++ multithreading qt


【解决方案1】:

我终于在这里找到了答案: 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()));

有人认为这种方法有什么问题吗?

【讨论】:

  • 对不起,响应迟了,但你想要那个信号,所以当 MyWorkerObjectrun() 方法完成时,它会发出 finished() 并且线程类会自行清理。本质上,您对this-&gt;thread()-&gt;quit() 所做的唯一事情就是杀死线程,您的MyWorkerObject 的r​​un 方法将在不进行清理的情况下终止。你最好有一个调用布尔值的插槽,告诉你的 MyWorkerObject Run() 方法终止,然后在终止时调用 finished() 并且 workerThread 也会自动清理。
  • 所以你会有workerObject::quit() { m_abort = true; } 方法并且会被称为:workerObject-&gt;quit(); workerThread-&gt;join()
【解决方案2】:

线程的目的是允许进程同时运行(同时!),所以如果你只是创建一个线程来工作并等待当前线程,你不需要使用新线程。

为了回答您关于 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() 函数,与所有其他非异步取消安全函数一样 函数,只能用延迟取消类型调用。

在禁用可取消状态下不能发生取消。

因此,只需要考虑默认的可取消状态。作为 指定,要么取消 pthread_join() 调用,要么成功, 但不是两者兼而有之。应用程序的区别是显而易见的,因为 运行取消处理程序或 pthread_join() 返回。那里 没有竞争条件,因为在延迟中调用了 pthread_join() 可取消状态。

如果一个实现检测到线程指定的值 pthread_join() 的参数不是指可连接线程,而是 建议该函数失败并报告 [EINVAL] 错误。

如果一个实现检测到线程指定的值 pthread_join() 的参数是指调用线程,它是 建议该函数失败并报告 [EDEADLK] 错误。

如果实现在其结束后检测到线程 ID 的使用 生命周期,建议函数失败并报告 [ESRCH] 错误。

【讨论】:

  • 梅林,那你会怎么做呢?工作线程包含一个工作对象(workerobject->movetothread(workerthread)),其工作是向代码中的不同线程发送一些信号,并通过其槽等待它们的响应。如果工作对象不包含在工作线程中,它怎么能从其他线程接收信号呢?它需要一个事件循环。也许我真正需要的是一个 QEventLoop,但我认为通过工作线程来做这件事是合适的。
  • @AliB,如果没有看到您当前的代码,很难给出建议,但听起来工作线程应该是您的主线程,假设您已将其创建为不同线程上的对象。当你调用 QApplication::exec() 时,主线程自动有一个事件循环。如果workerobject做的不多,但协调其他线程,那么在主线程上应该没问题。
  • 感谢 Merlin,添加了代码。我有一个循环正在运行,我想创建线程来完成每个项目的工作。我绝对可以以不同但不方便的方式做到这一点。但我想了解如何方便地做到这一点,而不必将我的循环分解为创建线程的成员变量,并且有信号来增加它等等......
  • workerThread的等待和删除是多余的。您将信号finished() 连接到workerThread 的deleteLater,它会在线程完成后删除线程。假设 for 循环在主线程上,您不需要调用 wait,只需让它在处理它可能接收到的任何事件时进行滴答。如果你想让一个线程结束,调用它的退出函数。
  • 另外,不要调用你想在你的workerObject上启动的函数,而是将线程的启动信号连接到workerObject中的一个槽。
【解决方案3】:

QThread::wait() 不是你需要的。这个函数就是你说的,它等待线程终止。

bool QThread::wait (unsigned long time = ULONG_MAX)

阻塞线程直到满足以下任一条件:

与此 QThread 对象关联的线程已完成执行(即当它 从 run()) 返回。如果线程已完成,此函数将返回 true。它也是 如果线程尚未启动,则返回 true。

时间毫秒已经过去。如果时间是 ULONG_MAX(默认),那么等待将 永不超时(线程必须从 run() 返回)。如果 等待超时。

如果您需要同步两个线程(您的主线程和创建的线程),那么我建议使用信号和插槽来指示哪个线程已准备好(触发 isReady 布尔值)并进行 while (!isReady) { sleep(1ms); processEvents(); } 循环。可能不是最好的方法,但应该可以。

【讨论】:

  • 其他人所说的也是如此;如果您一次只运行一组代码,那么您不需要线程。
猜你喜欢
  • 2013-11-11
  • 2014-03-01
  • 1970-01-01
  • 1970-01-01
  • 2018-11-19
  • 2014-05-23
  • 2014-09-21
  • 2017-06-19
  • 2020-06-06
相关资源
最近更新 更多