1. 多线程的基本概念
程序要完成两个任务:
- 任务 1 进行一项复杂的计算,需要 1 秒才能完成。
- 任务 2 读取磁盘,需要 1 秒才能完成。
我们可以串行的执行这两项任务,先执行任务 1,再执行任务 2,完成这两项任务总共需要 2 秒。
我们可以并行的执行这两项任务,同时执行这两项任务,完成这两项任务只需要 1 秒。
显然,并行执行的时间小于串行执行的时间。很多场景下,我们希望程序能够同时执行多个任务,操作系统提供了多线程的机制用于实现并行执行多个任务。在操作系统中,线程是一个可以独立执行的任务。程序执行时至少包含一个线程,可以使用线程相关的 API 创建新的线程。
Python 的 threading 模块提供了类 Thread,用户通过新建一个类 Thread 创建新的线程,本文描述了类 Thread 的基本使用。
2. 多线程的基本使用
Python 的 threading 模块中提供了类 Thread 用于实现多线程,用户有两种使用多线程的方式:
- 在线程构造函数中指定线程的入口函数。
- 自定义一个类,该类继承类 Thread,在自定义的类中实现 run 方法。
2.1 线程的构造函数和重要的成员方法
本节介绍 Thread 相关的三个函数的功能:
- 类 Thread 的构造函数
- 类 Thread 的 start 方法
- 类 Thread 的 join 方法
2.1.1 类Thread的构造函数
Thread(group = None, target = None, name = None, args = (), kwargs = {})
参数的含义如下:
- group: 线程组,目前还没有实现,在此处必须是 None。
- target: 线程的入口函数,线程从该函数开始执行。
- name: 线程名。
- args: 线程的入口函数的参数,以元组的形式传入。
- kwargs: 线程的入口函数的参数,以字典的形式传入。
使用 Thread 构造一个新线程时,必须指定 target 和 args 两个参数,target 为线程的入口,args 为线程入口函数的参数。
2.1.2 类 Thread 的 start 方法
start()
在线程对象的构造函数中 target 指定了线程入口函数,args 指定了线程入口函数的参数。线程对象的 start 方法使新线程开始执行,执行函数 target(args)。
2.1.3 类 Thread 的 join 方法
join()
调用线程对象的 start 方法后,新线程开始执行函数 target(args)。调用线程对象的 join 方法,主线程阻塞,等待新线程执行完毕。
2.2 指定线程的入口函数
下面通过一个具体的例子,说明通过指定线程的入口函数的方式使用多线程。
import time
import threading
def thread_entry(begin, end):
for i in range(begin, end):
time.sleep(1)
print(i)
t0 = threading.Thread(target = thread_entry, args = (1, 4))
t1 = threading.Thread(target = thread_entry, args = (101, 104))
t0.start()
t1.start()
t0.join()
t1.join()
-
在第 9 行和第 10 行,通过调用 Thread 的构造函数创建了两个线程。
-
在第 9 行,设定线程的入口函数为 thread_entry,传递给入口函数两个参数:1 和 4,新的线程将执行 thread_entry(1, 4),变量 t0 指向新创建的线程对象。
-
在第 10 行,设定线程的入口函数为 thread_entry,传递给入口函数两个参数:101 和 104,新的线程将执行 thread_entry(101, 104),变量 t1 指向新创建的线程对象。
-
在第 4 行到第 7 行,定义了线程入口函数,该函数的功能是打印在 [begin, end) 区间的整数,每打印一个整数,调用 time.sleep(1) 睡眠 1 秒钟。
-
在第 11 行,调用 start 方法启动线程 t0,t0 开始执行 thread_entry(1, 4)。
-
在第 12 行,调用 start 方法启动线程 t1,t1 开始执行 thread_entry(101, 104)。
-
在第 13 行和第 14 行,调用 join 方法,等待线程 t0 和 t1 执行完毕。
在第 9 行和第 10 行,通过调用 Thread 的构造函数创建了两个线程。
在第 9 行,设定线程的入口函数为 thread_entry,传递给入口函数两个参数:1 和 4,新的线程将执行 thread_entry(1, 4),变量 t0 指向新创建的线程对象。
在第 10 行,设定线程的入口函数为 thread_entry,传递