Many students have heard that modern operating systems such as Mac OS X, UNIX, Linux, Windows and others are all multi-tasking operating systems.
What exactly is “multi-tasking”? Simply put, it means the operating system can run multiple tasks at the same time. For example, you are browsing the web with a browser, listening to an MP3 player, and rushing to finish your homework in Word simultaneously—that is multi-tasking, with at least three tasks running at the same time. There are also many other tasks running quietly in the background without being displayed on the desktop.
Nowadays, multi-core CPUs are very common, but even single-core CPUs in the past could perform multi-tasking. Since CPUs execute code sequentially, how did single-core CPUs handle multi-tasking?
The answer is that the operating system alternates between tasks in turn: Task 1 runs for 0.01 seconds, then switches to Task 2, which runs for another 0.01 seconds, then switches to Task 3 for 0.01 seconds, and this cycle repeats. On the surface, each task is executed alternately, but because the CPU runs at an extremely fast speed, it feels like all tasks are running simultaneously.
True parallel execution of multi-tasking can only be achieved on multi-core CPUs. However, since the number of tasks is far greater than the number of CPU cores, the operating system will automatically schedule many tasks to run on each core in turn.
For the operating system, a task is a Process. For example, opening a browser launches a browser process, opening Notepad launches a Notepad process, opening two Notepad windows launches two Notepad processes, and opening Word launches a Word process.
Some processes need to handle more than one thing at a time. Take Word as an example: it can perform typing, spell checking, printing, and other operations simultaneously. To handle multiple things at once within a single process, we need to run multiple sub-tasks concurrently. These sub-tasks within a process are called Threads.
Since every process has to handle at least one task, a process has at least one thread. Of course, complex processes like Word can have multiple threads, which can run concurrently. The execution mode of multi-threading is the same as that of multi-processing: the operating system rapidly switches between multiple threads, allowing each thread to run alternately for short periods, making it seem like they are executing simultaneously. Of course, true concurrent execution of multi-threading can only be achieved with a multi-core CPU.
All the Python programs we have written so far are single-task processes—that is, they have only one thread. What if we need to execute multiple tasks at the same time?
There are two solutions:
There is, of course, a third approach: launch multiple processes, and each process starts multiple threads. This allows even more tasks to run concurrently, but this model is more complex and is rarely used in practice.
To summarize, there are three ways to implement multi-tasking:
When executing multiple tasks simultaneously, these tasks are usually not independent of each other—instead, they need to communicate and coordinate with one another. For instance, Task 1 may have to pause and wait for Task 2 to complete before it can continue, while Task 3 and Task 4 cannot run at the same time. Therefore, the complexity of multi-process and multi-thread programs is much higher than that of the single-process, single-thread programs we wrote earlier.
Due to their high complexity and difficulty in debugging, we do not want to write multi-task programs unless absolutely necessary. However, in many cases, multi-tasking is indispensable. Think about watching a movie on your computer: one thread must play the video, and another must play the audio. Otherwise, if implemented with a single thread, you would have to play the entire video first and then the audio, or vice versa—which is obviously impractical.
Python supports both multi-processing and multi-threading. We will discuss how to write these two types of multi-task programs.
A thread is the smallest unit of execution, while a process consists of at least one thread. The scheduling of processes and threads is completely determined by the operating system—the program itself cannot decide when to execute or how long to run.
Multi-process and multi-thread programs involve issues such as synchronization and data sharing, making them more complex to write.