likes
comments
collection
share

Python标准库 - threading(多线程处理)Python 在执行时,通常是采用同步的任务处理模式 ( 一个处

作者站长头像
站长
· 阅读数 20

Python 在执行时,通常是采用同步的任务处理模式 ( 一个处理完成后才会接下去处理第二个 ),然而 Python 的标准函数“threading”采用“线程”的方式,运用多个线程,在同一时间内处理多个任务 ( 非同步 ),这篇教程会介绍 threading 的用法。

同步与非同步

同步和非同步的常见说法是:“ 同步模式下,每个任务必须按照顺序执行,后面的任务必须等待前面的任务执行完成,在非同步模式下,后面的任务不用等前面的,各自执行各自的任务”,也可以想像成“ 同一条路 vs 不同的多条路”,通过道路的方式,会更容易明白同步和非同步。

  • 同步:“同一条路”,只能依序排队前进
  • 非同步:“不 ( 非 ) 同的多条路”,可以各走各的

Python标准库 - threading(多线程处理)Python 在执行时,通常是采用同步的任务处理模式 ( 一个处

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
评论
请登录