Python标准库 - threading(多线程处理)Python 在执行时,通常是采用同步的任务处理模式 ( 一个处
Python 在执行时,通常是采用同步的任务处理模式 ( 一个处理完成后才会接下去处理第二个 ),然而 Python 的标准函数“threading”采用“线程”的方式,运用多个线程,在同一时间内处理多个任务 ( 非同步 ),这篇教程会介绍 threading 的用法。
同步与非同步
同步和非同步的常见说法是:“ 同步模式下,每个任务必须按照顺序执行,后面的任务必须等待前面的任务执行完成,在非同步模式下,后面的任务不用等前面的,各自执行各自的任务”,也可以想像成“ 同一条路 vs 不同的多条路”,通过道路的方式,会更容易明白同步和非同步。
- 同步:“同一条路”,只能依序排队前进。
- 非同步:“不 ( 非 ) 同的多条路”,可以各走各的。
import threading
要使用 threading 必须先 import threading 库。
import threading
有没有使用 threading 的差异
在没有使用 threading 的状况下, 如果不同的函数里都有“循环”,则循环的执行会按照函数执行的顺序进行,以下方的代码为例,执行后会先打印出 aa() 函数的循环内容,执行完毕再打印出 bb() 函数的循环内容。
import time
def aa():
i = 0
while i<5:
i = i + 1
time.sleep(0.5)
print('A:', i)
def bb():
i = 0
while i<50:
i = i + 10
time.sleep(0.5)
print('B:', i)
aa() # 先执行 aa()
bb() # aa() 结束后才会执行 bb()
'''
A: 1
A: 2
A: 3
A: 4
A: 5
B: 10
B: 20
B: 30
B: 40
B: 50
'''
如果有使用 threading,则 两个函数就可以同时运行 ( 虽然是同时,但底层仍然是有几毫秒的执行顺序 )
import threading
import time
def aa():
i = 0
while i<5:
i = i + 1
time.sleep(0.5)
print('A:', i)
def bb():
i = 0
while i<50:
i = i + 10
time.sleep(0.5)
print('B:', i)
a = threading.Thread(target=aa) # 创建新的线程
b = threading.Thread(target=bb) # 创建新的线程
a.start() # 启用线程
b.start() # 启用线程
'''
A: 1
B: 10
A: 2
B: 20
A: 3
B: 30
A: 4
B: 40
A: 5
'''
threading 基本用法
创建 threading 的方法如下:
thread = threading.Thread(target=function, args)
# function 要在线程里执行的函数
# args 函数所需的引数,使用 tuple 格式,如果只有一个参数,格式 (参数,)
创建 threading 之后,就可以使用下列常用的方法:
方法 | 说明 |
---|---|
start() | 启用线程。 |
join() | 等待线程,直到该线程完成才会进行后续动作。 |
ident | 取得该线程的标识符。 |
native_id | 取得该线程的 id。 |
is_alive() | 线程是否启用,启用 True,否则 False。 |
下方的代码执行后,会将 aa()、bb() 和 cc() 三个函数分别创建为线程,接着当使用 a.start() 和 b.start() 方法启用后,因为有加入 a.join() 和 b.join() 的等待方法,所以 c.start() 会在 aa() 与 bb() 执行完成后,才会启用。
import threading
import time
def aa():
i = 0
while i<5:
i = i + 1
time.sleep(0.5)
print('A:', i)
def bb():
i = 0
while i<50:
i = i + 10
time.sleep(0.5)
print('B:', i)
def cc():
i = 0
while i<500:
i = i + 100
time.sleep(0.5)
print('C:', i)
a = threading.Thread(target=aa)
b = threading.Thread(target=bb)
c = threading.Thread(target=cc)
a.start()
b.start()
a.join() # 加入等待 aa() 完成的方法
b.join() # 加入等待 bb() 完成的方法
c.start() # 当 aa() 与 bb() 都完成后,才会开始执行 cc()
'''
A: 1
B: 10
A: 2
B: 20
A: 3
B: 30
A: 4
B: 40
A: 5
B: 50
C: 100 <--- A B 都结束后才开始
C: 200
C: 300
C: 400
C: 500
'''
如果只加入 a.join() 而不加入 b.join(),则 cc() 会在 aa() 执行结束就开始。
import time
def aa():
i = 0
while i<5:
i = i + 1
time.sleep(0.5)
print('A:', i)
def bb():
i = 0
while i<100:
i = i + 10
time.sleep(0.5)
print('B:', i)
def cc():
i = 0
while i<500:
i = i + 100
time.sleep(0.5)
print('C:', i)
a = threading.Thread(target=aa)
b = threading.Thread(target=bb)
c = threading.Thread(target=cc)
a.start()
b.start()
a.join() # 加入等待 aa() 完成的方法
c.start() # 当 aa() 完成后,就会开始执行 cc()
'''
A: 1
B: 10
A: 2
B: 20
A: 3
B: 30
A: 4
B: 40
A: 5
B: 50
C: 100 <--- A 结束就开始
B: 60
C: 200
B: 70
C: 300
B: 80
C: 400
B: 90
C: 500
B: 100
'''
Lock() 锁定
使用 threading 创建线程后, 可以使用 Lock() 方法创建一个线程的“锁”,当 Lock 创建后,就能使用 acquire()
方法锁定,使用 release()
方法解除锁定,如果有多个线程共用同一个 Lock,则同一时间只会执行第一个锁定的线程,其他的线程要等到解锁才能够执行。
以下方的代码为例,会先使用 threading.Lock() 创建一个 Lock,当 aa() 和 bb() 执行时会先使用 lock.acquire() 进行锁定 ( 以执行的顺序表示谁先锁定 ), 因为 aa() 比较先执行所以会先锁定,接着当 aa() 里的 i 等于 2 时使用 lock.release() 解除锁定,这时 bb() 就可以开始执行。
import threading
import time
def aa():
lock.acquire() # 锁定
i = 0
while i<5:
i = i + 1
time.sleep(0.5)
print('A:', i)
if i==2:
lock.release() # i 等于 2 时解除锁定
def bb():
lock.acquire() # 锁定
i = 0
while i<50:
i = i + 10
time.sleep(0.5)
print('B:', i)
lock.release()
lock = threading.Lock() # 创建 Lock
a = threading.Thread(target=aa)
b = threading.Thread(target=bb)
a.start()
b.start()
'''
A: 1
A: 2
B: 10
A: 3
B: 20
A: 4
B: 30
A: 5
B: 40
B: 50
'''
Event() 事件处理
threading 除了基本的多执行续功能,也提供 Event() 事件处理的方法,通过“事件”的方式,就能让不同的执行续之间彼此沟通,也能轻松做到“ 等待 A 线程完成某件事后,B 线程再继续”的功能,事件的处理包含下方几种方法:
方法 | 说明 |
---|---|
threading.Event() | 注册一个事件。 |
set() | 触发事件。 |
wait() | 等待事件被触发。 |
clear() | 清除事件触发,事件回到未被触发的状态。 |
下方的代码执行后,会注册一个 event 事件,当 aa() 执行时使用 event.wait() 等待事件被触发,接着设置 bb() 执行到 i 等于 30 的时候就会触发事件,这时 aa() 才会开始运行。
import threading
import time
def aa():
event.wait() # 等待事件被触发
event.clear() # 触发后将事件回归原本状态
for i in range(1,6):
print('A:',i)
time.sleep(0.5)
def bb():
for i in range(10,60,10):
if i == 30:
event.set() # 触发事件
print('B:',i)
time.sleep(0.5)
event = threading.Event() # 注册事件
a = threading.Thread(target=aa)
b = threading.Thread(target=bb)
a.start()
b.start()
'''
B: 10
B: 20
B: 30
A: 1
B: 40
A: 2
B: 50
A: 3
A: 4
A: 5
'''
下方的代码注册了两个事件,event_a 会在输入任意内容后触发,触发后就会打印出 1~5 的数字,打印出完成后会触发 event_b,这时才又可以继续输入文字,不断重复两个事件的触发与执行续的执行。
import threading
import time
def aa():
i = 0
while True:
event_a.wait() # 等待 event_a 被触发
event_a.clear() # 还原 event_a 状态
for i in range(1,6):
print(i)
time.sleep(0.5)
event_b.set() # 触发 event_b
def bb():
while True:
input('输入任意内容')
event_a.set() # 触发 event_a
event_b.wait() # 等待 event_b 被触发
event_b.clear() # 还原 event_b 状态
event_a = threading.Event() # 注册 event_a
event_b = threading.Event() # 注册 event_b
a = threading.Thread(target=aa)
b = threading.Thread(target=bb)
a.start()
b.start()
'''
输入任意内容 a
1
2
3
4
5
输入任意内容 b
1
2
3
4
5
输入任意内容
'''
小结
Python 的 threading 内置函数库是一个相当方便的函数库,不仅可以让原本同步的执行变成非同步,大幅减少工作时间,其中的 Event() 方法更是非常好用,相信对于非同步程序的编写,能有十足的帮助。
转载自:https://juejin.cn/post/7402845471646400552