首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >循环中QThread的正确使用

循环中QThread的正确使用
EN

Stack Overflow用户
提问于 2019-11-19 21:03:48
回答 2查看 510关注 0票数 0

我正在尝试更深入地使用QThread,但是我有一种感觉,我真的没有在循环中正确使用它。

我有一些耗时的计算,我需要运行几次。我正在尝试以下(简化的示例):

代码语言:javascript
运行
复制
for(int i = 0; i < 100; ++i)
{
    worker *task = new worker();

    connect(task, &worker::finished, this, &controller::calcFinished);
    connect(task, &worker::resultReady, this, &controller::handleResult);
    task->start();
}

run()函数如下所示:

代码语言:javascript
运行
复制
variable = 0;

for(int i = 0; i < 5000; ++i)
{
    for(int j = 0; j < 500; ++j)
    {
        for(int k = 0; k < 500; k++)
        {
            ++variable;
        }
    }
}

emit resultReady(variable);

关于这一点,我有几个问题:

  • 如何避免启动过多的线程?就像。如果我想将其数量限制为QThread::idealThreadCount()。
  • 如何检查每个线程是否完成?
  • 如何避免代码泄漏?

我尝试过QThreadPool,这似乎是对我的问题的一个解决方案,但从那里我无法收集线程中的计算结果。也许我漏掉了什么?

一些附加的注意事项:目前我正在使用重新实现run()函数的方法。我也尝试过moveToThread()方法,但我更迷失了方向。因此,如果这是解决办法,有些人可以尝试向我解释它是如何工作的。我正在检查来自Qt:https://doc.qt.io/qt-5/qthread.html的文档,但是在这里,我有一种感觉,在这个示例中甚至没有发出'operate‘信号(否则为什么需要连接它?)

谢谢你提前给我答案!

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2019-11-19 23:00:45

@abhlib是正确的,这正是提供给您的,并且应该非常适合您的需求。但是,我仍然敢澄清一些关于实现细节的特殊问题。

下面是一个简单的示例,它演示了Qt提供的一些基本方便类(不要在实际代码中那样使用它!):

代码语言:javascript
运行
复制
#include <QCoreApplication>

#include <QtConcurrent/QtConcurrentRun>
#include <QFutureSynchronizer>
#include <QFutureWatcher>

#include <QDebug>

long long job(int k)
{
    int variable = 0;

    for(int i = 0; i < 5000; ++i)
    {
        for(int j = 0; j < 500; ++j)
        {
            for(int k = 0; k < 500; k++)
            {
                ++variable;
            }
        }
    }

    return variable / k;
}

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    QThreadPool myPool;
    myPool.setMaxThreadCount(QThread::idealThreadCount()); // Here goes thread number
    qInfo() << QString{"Thread count: %1"}.arg(QThread::idealThreadCount());

    constexpr int TASK_COUNT = 100;
    QFutureSynchronizer<long long> futureSync; // waits for multiple futures
    QMap<QSharedPointer<QFutureWatcher<long long>>, QFuture<long long>> myResults;

    for (int i = 1; i <= TASK_COUNT; ++i)
    {
        QFuture<long long> task = QtConcurrent::run(&myPool, job, i);
        // a shared pointer 'coz it cannot be copied, simplified example
        QSharedPointer<QFutureWatcher<long long>> watcher(new QFutureWatcher<long long>()); 
        watcher->setFuture(task);

        myResults.insert(watcher, task);
        QObject::connect(watcher.get(), &QFutureWatcher<long long>::finished, [num = i, &myResults, watcher](){
            qInfo() << QString{"%1 %2"}.arg(num).arg(myResults[watcher].result());
        }); // waiting for particular result
    }

    // this way we do not return until everything is done
    futureSync.waitForFinished(); 
    return a.exec();
}

苏..。在处理线程时,通常有四个选项:

  1. 继承QThread
  2. 使用“QThread”+ moveToThread()
  3. 子类QRunnable和使用QThreadPool
  4. 只要使用QtConcurrent中的任何东西

我想,与解释我的示例(Qt文档是相当全面的)相比,您更感兴趣的是关于这些用法的一些提示。

继承QThread

这曾经是一种处理线程的老式方法,一段时间以前,它甚至表明了作者糟糕的编码风格。在这样的情况下工作得很好,比如“我们有一个后台线程和计算,在那里它做了一些事情”。您可以尝试它,这里有一个警告:您可以通过一些属于一个线程和另一个线程的方法轻松地创建一些复杂的类。IMHO,这就是调试地狱。

使用moveToThread()

这是实现后台计算的一种巧妙方法。您将得到一个具有独立事件循环的线程,所有使您困惑的管理都是通过信号和插槽完成的,请检查docs 这里

QtConcurrent帮手

嗯,这里有几个助手,它们都是比较高级的,而且大多数都实现了Map-Reduce多线程模式。关键的概念是,您有一个Qt线程池(由于某种原因,它是一个默认的全局线程池或您自己的线程池),并得到QFuture类的实例,这些实例以延迟的方式获得计算结果。

由于ar QFuture没有信号,还存在QFutureWatcher & QFutureSynchronizer类,它们为您提供有关任务状态的信号(实际上,您需要知道它们什么时候完成)。在一些更复杂的问题中,您可能希望报告和监视任务的进度,这也是可能的。QFuture还支持暂停/恢复/取消。

好的,这些复杂的选项需要更多的实现细节,但是好的旧QtConcurrent::run完全按照您的要求做了:它接收到一些可调用和返回的QFuture实例,这些实例不能暂停或取消,但仍然可以解除几乎所有“低级”线程管理。配置一个线程池,它向工作线程提供任务,剩下的就是等待结果的合适方式。试试看!

QRunnable

这几乎是同样的技巧,只是使用QThreadPool的另一种方便的方式。您可以使用与run()map()等相同的方式配置它。但是如果没有QFuture,您将缺乏一种非常方便的方法来处理结果和完成任务,这对于一些真正的自定义任务可能是有用的。这里的标准示例如下:

代码语言:javascript
运行
复制
  class HelloWorldTask : public QRunnable
  {
      void run() override
      {
          qDebug() << "Hello world from thread" << QThread::currentThread();
      }
  };

  HelloWorldTask *hello = new HelloWorldTask();
  // QThreadPool takes ownership and deletes 'hello' automatically
  QThreadPool::globalInstance()->start(hello);

方法的选择取决于您,但是您最好熟悉它们,这样您就能够根据所解决的问题改变线程工具。

至于内存泄漏,它是一个更一般的,不是特别多线程的主题。Practice & Valgrind可以帮助您度过难关,但是如果必须在这个主题上提供一些非常简短的建议,那么可以这样做:使用智能指针,检查Qt对象的所有权,如果无法避免,则适当保护多线程内存管理。

祝好运!

票数 1
EN

Stack Overflow用户

发布于 2019-11-19 21:55:22

使用来自QtConcurrent::run()框架的Qt Concurrent

代码语言:javascript
运行
复制
extern int aFunction();
QThreadPool pool;
QFuture<int> future = QtConcurrent::run(&pool, aFunction);

然后,可以从未来的对象中检索结果。

代码语言:javascript
运行
复制
int result = future.result();
票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/58942753

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档