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 你说得对,但这里的问题是QtConcurrent
和QThread
在切换时间上的区别。
取决于您的用例。与往常一样,测量一下它是否会对您的情况产生影响。 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),最终调用QThread
的start()
。
当然,这条链条并不完整,很长的路,不是吗?所以据我所知,当我们使用抽象时,我们有抽象惩罚(QtConcurrent
比QThread
有更大的惩罚),但最终结果是相同的,它是QThread
。
【讨论】:
谢谢。所以如果都是QThread
s 我猜这两者在开始跑之后差别不大。以上是关于QtConcurrent 与多线程 QThread 的多线程性能的主要内容,如果未能解决你的问题,请参考以下文章
Qt多线程:QtConcurrent + QFuture + QFutureWatcher