Multithreading Technologies in Qt
Qt中的多线程技术
Qt offers many classes and functions for working with threads. Below are four different approaches that Qt programmers can use to implement multithreaded applications.
Qt提供许多类和函数用于支持多线程工作。下面是Qt程序可用于多线程技术的四种方式。
QThread: Low-Level API with Optional Event Loops
QThread:对事件循环操作的低级别API
QThread is the foundation of all thread control in Qt. EachQThread instance represents and controls one thread.
QThread can either be instantiated directly or subclassed. Instantiating aQThread provides a parallel event loop, allowingQObject slots to be invoked in a secondary thread. Subclassing aQThread allows the application to initialize the new thread before starting its event loop, or to run parallel code without an event loop.
See the QThread class reference and the threading examples for demonstrations on how to useQThread.
QThread是线程控制的基础。每个QThread实例代表和控制一个线程。
QThread可以直接或通过子类实例化。实例化一个线程提供一个平行的事件循环,允许QObject槽函数在第二个线程中调用。QThread的子类允许程序在其事件循环前初始化新线程,或不使用事件循环而直接平行运行代码。
使用QThread,参见 QThread class reference 和 threading examples 文档。
QThreadPool and QRunnable: Reusing Threads
QThreadPool和QRunnable:重用线程
Creating and destroying threads frequently can be expensive. To reduce this overhead, existing threads can be reused for new tasks.QThreadPool is a collection of reuseable QThreads.
To run code in one of aQThreadPool's threads, reimplementQRunnable::run() and instantiate the subclassedQRunnable. UseQThreadPool::start() to put theQRunnable in theQThreadPool's run queue. When a thread becomes available, the code withinQRunnable::run() will execute in that thread.
Each Qt application has a global thread pool, which is accessible throughQThreadPool::globalInstance(). This global thread pool automatically maintains an optimal number of threads based on the number of cores in the CPU. However, a separateQThreadPool can be created and managed explicitly.
频繁的创建和销毁线程的开销高。为了降低这种开销,现有的线程可以被新任务重用。QThreadPool是可重用QThread的集合。
运行QTreadPool的一个线程的代码,重新实现QRunnable::run()以及实例化QRunnable子类。使用QThreadPool::start()将QRunnable放入QThreadPool的运行队列中。当一个线程可用时,在QRunnable::run()中的代码将会在这个线程中运行。
每个Qt程序都有一个全局线程池(global thread pool),可以通过QThreadPool::globalInstance()获得。这个全局线程池根据CPU的核心数,自动地维持线程的最佳个数。但是,一个单独的QTheadPool可以被明确的创建和管理。
Qt Concurrent: Using a High-level API
Qt Concurrent: 使用高级别API
The Qt Concurrent module provides high-level functions that deal with some common parallel computation patterns: map, filter, and reduce. Unlike usingQThread andQRunnable, these functions never require the use oflow-level threading primitives such as mutexes or semaphores. Instead, they return aQFuture object which can be used to retrieve the functions' results when they are ready.QFuture can also be used to query computation progress and to pause/resume/cancel the computation. For convenience,QFutureWatcher enables interactions withQFutures via signals and slots.
Qt Concurrent's map, filter and reduce algorithms automatically distribute computation across all available processor cores, so applications written today will continue to scale when deployed later on a system with more cores.
This module also provides theQtConcurrent::run() function, which can run any function in another thread. However,QtConcurrent::run() only supports a subset of features available to the map, filter and reduce functions. TheQFuture can be used to retrieve the function's return value and to check if the thread is running. However, a call toQtConcurrent::run() uses one thread only, cannot be paused/resumed/canceled, and cannot be queried for progress.
See the Qt Concurrent module documentation for details on the individual functions.
Qt Concurrent 模块提供……。与使用QThread和QRunnable不同,这些功能绝不需要使用低级的线程原语( threading primitives ),比如mutexes或semaphores。替代地,它们返回一个QFuture对象,可以在函数结果就绪时,检索之。QFuture也可以用于查询计算进展,暂停、重开、取消计算。为了方便,QFutureWatcher可以通过信号和槽函数与QFuture交互。
Qt Concurrent的map、filter和reduce算法自动分配计算到可用的处理器核心,所以……。
这个模块也提供QtConcurrent::fun()方法,可以用于在另外的线程中运行任何函数。但是QtConcurrent::run()只支持map,filter,和reduce功能的特性的一部分。QFuture可以用于检索函数的返回值,并检查线程是否在运行。但是,调用一次QtConcurrent::run()只使用一个线程,不能被暂停、重开、取消,也不能被进程查询。
详见……。
WorkerScript: Threading in QML
The WorkerScript QML type lets JavaScript code run in parallel with the GUI thread.
Each WorkerScript instance can have one .js script attached to it. When WorkerScript::sendMessage() is called, the script will run in a separate thread (and a separateQML context). When the script finishes running, it can send a reply back to the GUI thread which will invoke the WorkerScript::onMessage() signal handler.
Using a WorkerScript is similar to using a worker QObject that has been moved to another thread. Data is transferred between threads via signals.
See the WorkerScript documentation for details on how to implement the script, and for a list of data types that can be passed between threads.
Choosing an Appropriate Approach
选择一个合适的方案
As demonstrated above, Qt provides different solutions for developing threaded applications. The right solution for a given application depends on the purpose of the new thread and the thread's lifetime. Below is a comparison of Qt's threading technologies, followed by recommended solutions for some example use cases.
如上所示,Qt提供了开发多线程程序的多种解决方案。要根据新线程的目的和生命周期来决定适合的解决方案。下面是各个Qt多线程技术的对比,以及推荐的应用情况的例子。
Comparison of Solutions
Feature |
QThread |
QRunnable andQThreadPool |
QtConcurrent::run() |
Qt Concurrent (Map, Filter, Reduce) |
WorkerScript |
Language |
C++ |
C++ |
C++ |
C++ |
QML |
Thread priority can be specified 优先级是否可以指定 |
Yes |
Yes |
|||
Thread can run an event loop 是否可以运行事件循环 |
Yes |
||||
Thread can receive data updates through signals 线程是否可以通过信号更新数据 |
Yes (received by a worker QObject) |
Yes (received by WorkerScript) |
|||
Thread can be controlled using signals 线程是否可以通过信号控制 |
Yes (received by QThread) |
Yes (received by QFutureWatcher) |
|||
Thread can be monitored through a QFuture 线程是否可以通过QFuture监视 |
Partially |
Yes |
|||
Built-in ability to pause/resume/cancel |
Yes |
Example Use Cases
Lifetime of thread 线程生命周期 |
Operation 操作 |
Solution 解决方案 |
One call 一次性 |
Run a new linear function within another thread, optionally with progress updates during the run. 在新的线程中运行一个新的线性的函数,可在运行期间更新进度。 |
Qt provides different solutions: Qt提供了不同解决方案:
|
One call 一次性 |
Run an existing function within another thread and get its return value. 在新的线程运行一个已经存在的函数,并得到其返回值。 |
Run the function using QtConcurrent::run(). Have a QFutureWatcher emit the finished() signal when the function has returned, and callQFutureWatcher::result() to get the function's return value. 使用 QtConcurrent::run() 运行函数。当函数有返回值时, QFutureWatcher 会发送 finished() 信号,并调用 QFutureWatcher::result() 获得函数返回值。 |
One call 一次性 |
Perform an operation on all items of a container, using all available cores. For example, producing thumbnails from a list of images. 需要操作一个容器中所有的项。使用处理器所有可用的核心。一个常见的例子是从图像列表生成缩略图。 |
Use Qt Concurrent's QtConcurrent::filter() function to select container elements, and theQtConcurrent::map() function to apply an operation to each element. To fold the output into a single result, useQtConcurrent::filteredReduced() andQtConcurrent::mappedReduced() instead. 使用 Qt Concurrent 的 QtConcurrent::filter() 选择包含的元素, QtConcurrent::map() 将会把操作应用到所选的每个元素。要将输出折叠到一个信号结果,使用QtConcurrent::filteredReduced() 和 QtConcurrent::mappedReduced() 作为替代。 |
One call/Permanent 一次性/永久 |
Perfrom a long computation in a pure QML application, and update the GUI when the results are ready. |
Place the computation code in a .js script and attach it to a WorkerScript instance. Call sendMessage() to start the computation in a new thread. Let the script call WorkerScript::sendMessage() too, to pass the result back to the GUI thread. Handle the result inonMessage and update the GUI there. |
Permanent 永久 |
Have an object living in another thread that can perform different tasks upon request and/or can receive new data to work with. 需要一个对象在另一个线程生存,可以执行各种不同的任务请求以及/或可以接收新的数据用来处理。 |
Subclass a QObject to create a worker. Instantiate this worker object and aQThread. Move the worker to the new thread. Send commands or data to the worker object over queued signal-slot connections. 继承QObject创建工作者类。实例化工作者和一个 QThread 。将工作者移入新线程。通过队列信号槽连接,给工作者发送命令或数据。 |
Permanent 永久 |
Repeatedly perform an expensive operation in another thread, where the thread does not need to receive any signals or events. 在新线程中重复地执行繁重的操作,而不需要接收任何信号和事件。 |
Write the infinite loop directly within a reimplementation ofQThread::run(). Start the thread without an event loop. Let the thread emit signals to send data back to the GUI thread. 直接在 QThread::run() 写一个死循环。不使用事件循环,运行线程。让线程将数据通过发送信号发送给GUI线程。 |