等待QThread时UI会阻塞/如何正确使用QThread

Posted

技术标签:

【中文标题】等待QThread时UI会阻塞/如何正确使用QThread【英文标题】:Will UI block when waiting for QThread / How to use QThread properly 【发布时间】:2019-03-25 10:06:34 【问题描述】:

如果我在 ui 线程(窗口)中有一个进度条,它将无限运行,直到一个方法完成它的工作,如果我正在等待第二个 QThread 完成,ui 线程会阻塞并因此进度条吗?如果 ui 线程阻塞等待,那么我不会等待第二个线程。我想实现一个回调方法,该方法将在第二个线程完成时调用,但是:我如何连接到回调方法?

我想做什么? 我有一个窗口,这个窗口有一个进度条,它首先是不可见的。当用户按下某个按钮请求数据时,将调用一个方法,该方法返回一个 RequestPointer,其中包含一个返回请求状态的方法。 当用户按下按钮时,我想让进度条可见,无限运行直到请求完成,我可以将数据打印到窗口。 我想将这个指针传递给 Worker,并且 Worker 在一段时间(标志)循环中检查状态是否仍在运行,如果是,则休眠。当工作人员完成时,我想停止进度条并使其再次不可见。我必须将进度条传递给线程,还是我可以等待线程而不阻塞ui?

我不是 Qt 专业人士。对它真的很陌生。我试图从https://doc.qt.io/Qt-5/qthread.html 网站获取一些信息,但我有点难以理解代码示例。

我的工人阶级中的方法:

void Worker::watchRequest(RequestPtr r_ptr)

    bool exit = true;

    while (!exit)
    
        ErrorCode errorCode = r_ptr->Test();

        switch (errorCode)
        
        case Request_RUNNING:   
            QThread::msleep(10);
            break;
        case Request_ABORTED:
            exit = true;
            break;
        case Request_SUCCESS:
            exit = true;
            break;
        

    

【问题讨论】:

【参考方案1】:

QThread 有一个finished 信号。将此连接到某个适当的插槽,这将触发线程完成所需的任何操作。

我想知道进展情况的最佳人选是工作线程本身。您可以创建自己的信号,将当前进度发送到某个插槽,以便适当地更新进度条。

或者,您可以使用QTimer 不时读取当前进度(这类似于您现在拥有的,但不会阻塞 UI)。

【讨论】:

【参考方案2】:

如果您不想阻止用户界面,您只需在 while 循环中调用 QApplication::processEvents();

我有一些使用 std::future 而不是 QThread 的代码,我的代码如下所示:

while (!progressIndicator->UserBreak()
    && (future.wait_for(std::chrono::seconds(0)) != std::future_status::ready))

    QApplication::processEvents();

这很好用。

【讨论】:

所以,你的意思不是说:QThread::msleep(10) 我需要说 QApplication::processEvents()? 我认为这充其量是一种解决方法。最好根本不睡觉,让正常的事件循环运行,不时注入一些事件…… @Gino Pensuni - 我会两者都做:等待很短的时间(这是std::future::wait_for 在我的示例中使用非零参数所做的事情)然后处理事件。这提供了让用户中断进程并对此做出反应的可能性(在我的示例中为!progressIndicator->UserBreak())。 在我看来,忙等待在这里不必要地浪费 CPU 周期。此外,使用processEvents() 要求您的代码是可重入的(如果再次触发调用此函数的同一事件怎么办?这可能会导致不需要的递归)。它不提供任何额外的中断可能性(这在不忙等待时自然是可能的)。相反,应该考虑应用程序中所有可能的状态并触发它们之间的转换以响应事件。请参阅@Aconcagua 's answer 作为我的意思的示例。【参考方案3】:

要在操作运行时更新 UI 线程的进度条,请使用 QTimer 对象来增加进度条的值(最大值将为:操作完成时进度条的值小一)。还可以通过 Signal/Slot 方法将 QThread 连接到插槽,以在操作结束时向 UI 线程发出信号。当 QThread 完成操作后,向 UI Thread 中的 Slot 发送一个信号,Slot 将设置进度条的最终值,同时停止 QTimer。

【讨论】:

以上是关于等待QThread时UI会阻塞/如何正确使用QThread的主要内容,如果未能解决你的问题,请参考以下文章

QThread 阻塞主应用程序

PySide2 QThread 正在冻结 UI

在不阻塞 UI 执行的情况下等待几秒钟

除了主 ui 线程之外,使用 QThread 设置图形时遇到问题

我不确定为啥我的 QThread 模式会阻塞

qthreadnew不释放