Event原理是在线程中立了一个Flag,默认值是False,当一个或多个线程遇到event.wait()方法时阻塞,直到Event内部Flag值变为True.
Event 提供了如下方法:
- is_set():该方法返回 Event 的内部旗标是否为True。
- set():该方法将会把 Event 的内部旗标设置为 True,并唤醒所有处于等待状态的线程。
- clear():该方法将 Event 的内部旗标设置为 False,通常接下来会调用 wait() 方法来阻塞当前线程。
- wait(timeout=None):该方法会阻塞当前线程。
import threading
import time
event = threading.Event()
def cal(name):
# 等待事件,进入等待阻塞状态
print('%s 启动' % threading.currentThread().getName())
print('%s 准备开始计算状态' % name)
event.wait() # ①
# 收到事件后进入运行状态
print('%s 收到通知了.' % threading.currentThread().getName())
print('%s 正式开始计算!'% name)
# 创建并启动两条,它们都会①号代码处等待
threading.Thread(target=cal, args=('甲', )).start()
threading.Thread(target=cal, args=("乙", )).start()
time.sleep(2) #②
print('------------------')
# 发出事件
print('主线程发出事件')
event.set()
结果
Thread-1 启动
甲 准备开始计算状态
Thread-2 启动
乙 准备开始计算状态
------------------
主线程发出事件
Thread-1 收到通知了.Thread-2 收到通知了.
甲 正式开始计算!乙 正式开始计算!
cal() 函数在 ① 号代码处调用了 Event 的 wait(),因此两个线程执行到 ① 号代码处都会进入阻塞状态;即使主线程在 ② 号代码处被阻塞,两个子线程也不会向下执行。
直到主程序执行到最后一行,程序调用了 Event 的 set() 方法将 Event 的内部旗标设直为 True,并唤醒所有等待的线程,这两个线程才能向下执行。
import threading
class Account:
# 定义构造器
def __init__(self, account_no, balance):
# 封装账户编号、账户余额的两个成员变量
self.account_no = account_no
self._balance = balance
self.lock = threading.Lock()
self.event = threading.Event()
# 因为账户余额不允许随便修改,所以只为self._balance提供getter方法
def getBalance(self):
return self._balance
# 提供一个线程安全的draw()方法来完成取钱操作
def draw(self, draw_amount):
# 加锁
self.lock.acquire()
# 如果Event内部旗标为True,表明账户中已有人存钱进去
if self.event.is_set():
# 执行取钱操作
print(threading.current_thread().name
+ " 取钱:" + str(draw_amount))
self._balance -= draw_amount
print("账户余额为:" + str(self._balance))
# 将Event内部旗标设为False
self.event.clear()
# 释放加锁
self.lock.release()
# 阻塞当前线程阻塞
self.event.wait()
else:
# 释放加锁
self.lock.release()
# 阻塞当前线程阻塞
self.event.wait()
def deposit(self, deposit_amount):
# 加锁
self.lock.acquire()
# 如果Event内部旗标为False,表明账户中还没有人存钱进去
if not self.event.is_set():
# 执行存款操作
print(threading.current_thread().name\
+ " 存款:" + str(deposit_amount))
self._balance += deposit_amount
print("账户余额为:" + str(self._balance))
# 将Event内部旗标设为True
self.event.set()
# 释放加锁
self.lock.release()
# 阻塞当前线程阻塞
self.event.wait()
else:
# 释放加锁
self.lock.release()
# 阻塞当前线程阻塞
self.event.wait()
# 定义一个函数,模拟重复max次执行取钱操作
def draw_many(account, draw_amount, max):
for i in range(max):
account.draw(draw_amount)
# 定义一个函数,模拟重复max次执行存款操作
def deposit_many(account, deposit_amount, max):
for i in range(max):
account.deposit(deposit_amount)
# 创建一个账户
acct = Account("1234567" , 0)
# 创建、并启动一个“取钱”线程
threading.Thread(name="取钱者", target=draw_many,
args=(acct, 800, 100)).start()
# 创建、并启动一个“存款”线程
threading.Thread(name="存款者甲", target=deposit_many,
args=(acct , 800, 100)).start();
threading.Thread(name="存款者乙", target=deposit_many,
args=(acct , 800, 100)).start()
threading.Thread(name="存款者丙", target=deposit_many,
args=(acct , 800, 100)).start()
结果
存款者甲 存款:800
账户余额为:800
取钱者 取钱:800
账户余额为:0
存款者乙 存款:800
账户余额为:800
取钱者 取钱:800
账户余额为:0
结合 Event 的内部旗标,实现前面的 Account 的生产者-消费者效果:存钱线程(生产者)存钱之后,必须等取钱线程(消费者)取钱之后才能继续向下执行。
注
Event 实际上优点类似于 Condition 和旗标的结合体,但 Event 本身并不带 Lock 对象,因此如果要实现线程同步,还需要额外的 Lock 对象。