在本教程中,您将学习如何使用 Python 的内置线程模块探索 Python 的多线程功能。
了解多线程在 Python 中的工作原理,从进程和线程的基础知识开始,了解并发和并行的概念。接下来,了解如何使用内置threading模块在 Python 中启动和运行一个或多个线程。
让我们开始吧。
进程和线程:区别
流程是怎样的?
进程是需要运行的程序的实例。
它可以是任何东西,从 Python 脚本和 Web 浏览器(如 Chrome)到视频会议应用程序。您可以通过在计算机上启动任务管理器并导航到性能–> CPU来查看当前正在 CPU 内核上运行的进程和线程。

了解进程和线程
在内部,进程有专用的内存来存储与进程相对应的代码和数据。
一个进程由一个或多个线程组成。线程是操作系统可以执行的最小指令序列,代表执行流程。
每个线程都有自己的堆栈和寄存器,但没有专用的内存。与该进程关联的所有线程都可以访问该数据。因此,数据和内存由进程的所有线程共享。

具有 N 个核心的 CPU 可以同时并行运行 N 个进程。但是,同一个进程的两个线程不能同时运行,但是可以并发运行。以下各节解释并发和并行的概念。
根据到目前为止所学的知识,我们来总结一下进程和线程之间的区别。
| 特征 | 过程 | 线 |
| 记忆 | 专用内存 | 共享内存 |
| 执行模式 | 并行的,同时的 | 同时;但不是并行 |
| 执行处理人 | 操作系统 | CPython解释器 |
Python 中的多线程
在Python中, 全局解释器锁(GIL)确保任何时候只有一个线程可以获得锁并执行。所有线程都必须获得该锁才能执行。这允许任何时候只运行一个线程,避免同时多线程。
例如,考虑同一进程中的两个线程t1和t2 。线程共享相同的数据,因此当t1读取特定值k时, t2可能会更改相同的值k 。这可能会导致僵局和不良结果。但是,任何时候只有一个线程可以获得锁并执行。因此,GIL也保证了线程安全。
那么我们如何在Python中实现多线程呢?为了理解这一点,我们先讨论一下并发和并行的概念。
并发和并行:概述
考虑具有多个核心的 CPU。在下图中,CPU 有四个核心。这意味着可以随时并行执行四个不同的操作。
如果有四个进程,则每个进程可以在四个核心上独立且同时运行。假设每个进程有两个线程。

为了了解线程如何工作,让我们从多核处理器架构切换到单核处理器架构。如前所述,在给定的执行实例中只能有一个线程处于活动状态。然而,处理器核心可以切换线程。

例如,I/O 绑定线程经常等待 I/O 操作,例如读取用户输入、读取数据库和文件操作。在此等待时间内,I/O 绑定线程会释放其锁以允许其他线程运行。等待时间也可以是一个简单的操作,比如休眠n秒。
摘要:在等待操作期间,线程释放锁,从而允许处理器核心切换到另一个线程。一旦等待期结束,前一个线程就会恢复执行。处理器核心同时在线程之间切换的过程有利于多线程处理。 ✅
如果您想在应用程序中实现进程级并行性,请考虑使用多处理。
Python 线程模块:第一步
Python 附带了一个threading模块,您可以将其导入到 Python 脚本中。
import threading要在 Python 中创建线程对象,可以使用Thread构造函数threading.Thread(...) 。对于大多数线程实现来说,这是足够通用的语法。
threading.Thread(target=...,args=...)这里,
-
target是一个关键字参数,指示 Python 可调用对象。 -
args是目标接收的参数元组。
运行本教程中的代码示例需要 Python 3.x。 下载代码并按照步骤操作。
在 Python 中定义和运行线程
让我们定义将执行目标函数的线程。
目标函数是some_func 。
import threading
import time
def some_func():
print("Running some_func...")
time.sleep(2)
print("Finished running some_func.")
thread1 = threading.Thread(target=some_func)
thread1.start()
print(threading.active_count())我们来分析一下上面的代码片段做了什么。
- 导入
threading和time模块。 - 函数
some_func有一个解释性的print()语句,并包含 2 秒的睡眠操作。time.sleep(n)使函数休眠n秒。 - 接下来,使用目标
some_func定义线程thread_1。threading.Thread(target=...)创建一个线程对象。 - 注意:指定函数的名称,而不是函数调用。使用
some_func而不是some_func()。 - 创建线程对象并不启动线程。当您调用线程对象的
start()方法时执行。 - 要获取活动线程数,请使用
active_count()函数。
Python 脚本在主线程上运行,并创建另一个运行函数some_func的线程 ( thread1 )。因此,正如您在输出中看到的,活动线程数为 2。
# Output
Running some_func...
2
Finished running some_func.如果仔细观察输出,您会发现第一个 print 语句是在thread1启动时执行的。然而,在睡眠操作期间,处理器不会等待thread1完成执行,而是切换到主线程并打印活动线程的数量。

等待线程完成执行
如果想让thread1结束执行,可以在启动线程后调用join()方法。这样做会等到thread1完成执行而不会切换到主线程。
import threading
import time
def some_func():
print("Running some_func...")
time.sleep(2)
print("Finished running some_func.")
thread1 = threading.Thread(target=some_func)
thread1.start()
thread1.join()
print(threading.active_count())在这里, thread1在打印活动线程数之前完成了执行。所以只有主线程在运行。即活动线程数为1。 ✅
# Output
Running some_func...
Finished running some_func.
1如何在Python中运行多线程
接下来,让我们创建两个执行两个不同功能的线程。
这里, count_down是一个函数,它接受一个数字作为参数,并从该数字倒数到 0。
def count_down(n):
for i in range(n,-1,-1):
print(i)定义count_up 。这是另一个从零计数到指定数字的 Python 函数。
def count_up(n):
for i in range(n+1):
print(i)📑
range()函数与range(start, stop, step)语法一起使用时,默认stop终点站。– 要从特定数字倒数到零,请使用负
step值 -1 并将stop值设置为 -1 以包含零。– 同样,要计数到
n,stop值必须设置为n + 1。start和step的默认值分别是0和1,因此可以使用range(n + 1)来获取从0到n的序列。
接下来,定义两个线程thread1和thread2分别执行count_down和count_up函数。向这两个函数添加print语句和sleep操作。
请注意,创建线程对象时,必须在args参数中将目标函数参数指定为元组。这两个函数( count_down和count_up )都采用一个参数,因此您必须在值后面显式插入一个逗号。这会将后续元素推断为None ,因此参数仍作为元组传递。
import threading
import time
def count_down(n):
for i in range(n,-1,-1):
print("Running thread1....")
print(i)
time.sleep(1)
def count_up(n):
for i in range(n+1):
print("Running thread2...")
print(i)
time.sleep(1)
thread1 = threading.Thread(target=count_down,args=(10,))
thread2 = threading.Thread(target=count_up,args=(5,))
thread1.start()
thread2.start()在输出中,它看起来像这样:
- 函数
count_up在thread2中执行,从0开始计数到5。 -
count_down函数在thread1上运行并从 10 倒数到 0。
# Output
Running thread1....
10
Running thread2...
0
Running thread1....
9
Running thread2...
1
Running thread1....
8
Running thread2...
2
Running thread1....
7
Running thread2...
3
Running thread1....
6
Running thread2...
4
Running thread1....
5
Running thread2...
5
Running thread1....
4
Running thread1....
3
Running thread1....
2
Running thread1....
1
Running thread1....
0可以看到thread1和thread2交替运行,因为它们都需要等待操作(睡眠)。当count_up函数计数到 5 时, thread2不再处于活动状态。因此,您将仅获得与thread1相对应的输出。
概括
在本教程中,您学习了如何使用 Python 的内置线程模块实现多线程。要点总结如下。
- 您可以使用Thread构造函数创建线程对象。 threading.Thread(target=<callable>,args=(<tuple of args>))创建一个线程,该线程使用args指定的参数运行目标可调用对象。
- Python程序在主线程上运行,因此您创建的线程对象是附加线程。调用active_count()函数会返回任何实例中的活动线程数。
- 您可以使用线程对象的start()方法启动线程,并使用join()方法等待它执行完毕。
您可以通过调整等待时间或尝试不同的 I/O 操作来编写其他示例。请务必在未来的 Python 项目中实现多线程。祝你编码愉快!🎉




![2021 年如何设置 Raspberry Pi Web 服务器 [指南]](https://i0.wp.com/pcmanabu.com/wp-content/uploads/2019/10/web-server-02-309x198.png?w=1200&resize=1200,0&ssl=1)

