线程是并行地做事情，仅仅像进程。那么，线程与进程有何不同？当在电子表格上计算时，可能还有媒体播放器在同一台式机上运行，播放您最喜爱的歌曲。这是并行工作的 2 个进程的范例：一个运行电子表格程序；另一个运行媒体播放器。为此，多任务处理是众所周知的术语。仔细观察媒体播放器会发现，在一个单进程中还有事情在并行。当媒体播放器将音乐发送给音频驱动器时，带有所有铃声和哨子的用户界面还在不断更新。这就是线程 -- 在一个单进程内并发。
那么，如何实现并发呢？在单核 CPU 上并行工作是一种错觉，有点类似于电影中移动图像的错觉。对于进程，错觉是在很短时间后中断处理器在一进程中的工作而产生的。然后，处理器继续处理下一进程。为在进程之间切换，保存当前程序计数器，并加载下一处理器的程序计数器。这还不够，因为需要对寄存器、某些体系结构及特定 OS 数据执行相同操作。
就像一个 CPU 可以驱动 2 个或多个进程，也可以让 CPU 运行在一个单进程的 2 个不同代码段上。当进程启动时，它总是执行一代码段，因此进程被称为拥有一线程。不管怎样，程序可能决定启动第 2 线程。然后，在一个进程内同时处理 2 个不同的代码序列。通过重复保存程序计数器和寄存器，然后加载下一线程的程序计数器和寄存器，在单核 CPU 上达成并发。在活动线程之间循环，不需要程序的合作。当切换到下一线程发生时，线程可能处于任何状态。
CPU 设计的当前趋势是拥有多个核心。典型的单线程应用程序只能使用一个核心。不管怎样，可以将具有多个线程的程序赋值给多个核心，从而使事情以真正的并发方式发生。结果，将工作分发给多个线程可以使程序在多核 CPU 上运行得更快，因为可以使用其它核心。
As mentioned, each program has one thread when it is started. This thread is called the "main thread" (also known as the "GUI thread" in Qt applications). The Qt GUI must run in this thread. All widgets and several related classes, for example QPixmap , don't work in secondary threads. A secondary thread is commonly referred to as a "worker thread" because it is used to offload processing work from the main thread.
Each thread has its own stack, which means each thread has its own call history and local variables. Unlike processes, threads share the same address space. The following diagram shows how the building blocks of threads are located in memory. Program counter and registers of inactive threads are typically kept in kernel space. There is a shared copy of the code and a separate stack for each thread.
If two threads have a pointer to the same object, it is possible that both threads will access that object at the same time and this can potentially destroy the object's integrity. It's easy to imagine the many things that can go wrong when two methods of the same object are executed simultaneously.
Sometimes it is necessary to access one object from different threads; for example, when objects living in different threads need to communicate. Since threads use the same address space, it is easier and faster for threads to exchange data than it is for processes. Data does not have to be serialized and copied. Passing pointers is possible, but there must be a strict coordination of what thread touches which object. Simultaneous execution of operations on one object must be prevented. There are several ways of achieving this and some of them are described below.
So what can be done safely? All objects created in a thread can be used safely within that thread provided that other threads don't have references to them and objects don't have implicit coupling with other threads. Such implicit coupling may happen when data is shared between instances as with static members, singletons or global data. Familiarize yourself with the concept of thread safe and reentrant classes and functions.
There are basically two use cases for threads:
Developers need to be very careful with threads. It is easy to start other threads, but very hard to ensure that all shared data remains consistent. Problems are often hard to find because they may only show up once in a while or only on specific hardware configurations. Before creating threads to solve certain problems, possible alternatives should be considered.
|QEventLoop::processEvents ()||调用 QEventLoop::processEvents () repeatedly during a time-consuming calculation prevents GUI blocking. However, this solution doesn't scale well because the call to processEvents() may occur too often, or not often enough, depending on hardware.|
|QTimer||Background processing can sometimes be done conveniently using a timer to schedule execution of a slot at some point in the future. A timer with an interval of 0 will time out as soon as there are no more events to process.|
|QSocketNotifier QNetworkAccessManager QIODevice::readyRead ()||This is an alternative to having one or multiple threads, each with a blocking read on a slow network connection. As long as the calculation in response to a chunk of network data can be executed quickly, this reactive design is better than synchronous waiting in threads. Reactive design is less error prone and energy efficient than threading. In many cases there are also performance benefits.|
In general, it is recommended to only use safe and tested paths and to avoid introducing ad-hoc threading concepts. The QtConcurrent module provides an easy interface for distributing work to all of the processor's cores. The threading code is completely hidden in the QtConcurrent framework, so you don't have to take care of the details. However, QtConcurrent can't be used when communication with the running thread is needed, and it shouldn't be used to handle blocking operations.
见 Qt 中的多线程技术 page for an introduction to the different approaches to multithreading to Qt, and for guidelines on how to choose among them.
The following sections describe how QObjects interact with threads, how programs can safely access data from multiple threads, and how asynchronous execution produces results without blocking a thread.
As mentioned above, developers must always be careful when calling objects' methods from other threads. Thread affinity does not change this situation. Qt documentation marks several methods as thread-safe. postEvent() is a noteworthy example. A thread-safe method may be called from different threads simultaneously.
In cases where there is usually no concurrent access to methods, calling non-thread-safe methods of objects in other threads may work thousands of times before a concurrent access occurs, causing unexpected behavior. Writing test code does not entirely ensure thread correctness, but it is still important. On Linux, Valgrind and Helgrind can help detect threading errors.
When writing a multithread application, extra care must be taken to avoid data corruption. See 同步线程 for a discussion on how to use threads safely.
One way to obtain a worker thread's result is by waiting for the thread to terminate. In many cases, however, a blocking wait isn't acceptable. The alternative to a blocking wait are asynchronous result deliveries with either posted events or queued signals and slots. This generates a certain overhead because an operation's result does not appear on the next source line, but in a slot located somewhere else in the source file. Qt developers are used to working with this kind of asynchronous behavior because it is much similar to the kind of event-driven programming used in GUI applications.
Threading is a very complicated subject. Qt offers more classes for threading than we have presented in this tutorial. The following materials can help you go into the subject in more depth: