QtConcurrent 与多线程 QThread 的多线程性能

Posted

技术标签:

【中文标题】QtConcurrent 与多线程 QThread 的多线程性能【英文标题】:Multithreading performance of QtConcurrent Vs QThread with many threads 【发布时间】:2015-08-21 05:17:14 【问题描述】:

假设您的应用程序需要在多个线程中运行一个函数,其数量超过 CPU 内核/线程的数量。一种方法是使用QtConcurrent 并设置最大线程数:

MyClass *obj = new MyClass;

QThreadPool::globalInstance()->setMaxThreadCount(30);

for(int i=0;i<30;i++)
    QtConcurrent::run(obj, &MyClass::someFunction);

另一种方法是拥有多个对象并使用moveToThread 将它们移动到不同的线程:

for(int i=0;i<30;i++)

        MyClass *obj = new MyClass;
        QThread *th = new QThread();
        obj->moveToThread(th);
        connect(th, SIGNAL(started()), obj, SLOT(someFunction()) );
        connect(obj, SIGNAL(workFinished()), th, SLOT(quit()) );
        connect(th, SIGNAL(finished()), obj, SLOT(deleteLater()) );
        connect(th, SIGNAL(finished()), th, SLOT(deleteLater()) );

        th->start();

由于线程数多于CPU核数,因此运行时需要在不同核之间切换线程。

问题是这两种方法是否有不同的表现?即QThread 的切换与使用QtConcurrent::run 运行的切换不同吗?

【问题讨论】:

这取决于每个线程做什么。经验法则:对于 CPU 绑定的线程,线程数多于内核数会降低整体性能;对于 I/O 绑定线程,线程可能比可用内核多。你需要测量。 @RichardCritten 你说得对,但这里的问题是QtConcurrentQThread在切换时间上的区别。 取决于您的用例。与往常一样,测量一下它是否会对您的情况产生影响。 QConcurrent 是围绕 QThreads 的便利,区别在于任务的调度和资源利用方式,而不是上下文切换时间。编写自己的解决方案会给您带来更大的灵活性,但当然也会带来更多的工作量和更多的错误。 @FrankOsterfeld 但是这里所有的资源利用率、调度、.. 都是相同的,因为 30 个线程运行相同的功能。只有启动线程的方法不同。所以我认为唯一的区别是两者之间的上下文切换有何不同。 【参考方案1】:

简短回答:这取决于工作负载的性质/逻辑。

QtConcurrent 运行一个线程池,它是一个更高级别的 API 不适合运行大量的阻塞操作:如果你做了很多阻塞操作,你很快就会结束耗尽池并让其他请求排队。在这种情况下,QThread(较低级别的构造)可能更适合该操作(每个代表一个线程)。

【讨论】:

在我提供的示例中,线程池中的最大线程数设置为更大的数字,因此不会被耗尽,并且所有线程都在不排队的情况下运行。此外,这两种方法的过程的性质/逻辑是相同的,它们运行的​​是同一段代码。 @Nejat '大数字'是相对于您设置的最大数字。此问题中未指定工作负载的性质,因此没有足够的信息来提供任何分析。您必须自己进行基准测试。【参考方案2】:

我同意第一个答案,但我想补充一点。

QThread 是低级类,仅运行特定于操作系统的功能。 QtConcurrent 是什么?答案在Qt 源代码中。

第一级:run

QFuture<T> run(T (*functionPointer)())  

        return (new StoredFunctorCall0<T, T (*)()>(functionPointer))->start();

Second:

struct StoredFunctorCall0: public RunFunctionTask<T>     ...

Third:

template <typename T>
class RunFunctionTaskBase : public QFutureInterface<T> , public QRunnable
 ...

现在关于QRunnable。当我们以 QThreadPool 开始 QRunnable 时,我们这样做:

start() 调用tryStart() 调用startThread()QThreadPoolThread 操作(它是QThread subclass),最终调用QThreadstart()

当然,这条链条并不完整,很长的路,不是吗?所以据我所知,当我们使用抽象时,我们有抽象惩罚(QtConcurrentQThread 有更大的惩罚),但最终结果是相同的,它是QThread

【讨论】:

谢谢。所以如果都是QThreads 我猜这两者在开始跑之后差别不大。

以上是关于QtConcurrent 与多线程 QThread 的多线程性能的主要内容,如果未能解决你的问题,请参考以下文章

Qt多线程:QtConcurrent + QFuture + QFutureWatcher

qt创建线程 实用的方法

是否可以将 QtConcurrent::run() 与类的函数成员一起使用

如何设置 QTConcurrent 操作的最大线程数?

QtConcurrent 运行的线程 id

停止由 QtConcurrent::run 启动的线程?