线程调度由操作系统完成,存在一定的随机性无法准确的控制线程的轮换执行,通过线程通信可以达到该效果
lock=None
参数,程序创建Condition的时候通过lock参数传入要绑定的Lock对象,如果没有传该参数,则创建Condition的时候会自动创建一个与它绑定的Lock对象该类提供了如下常用方法:
import threading
class Account:
# 定义构造器
def __init__(self, account_no, balance):
# 封装账户编号、账户余额的两个成员变量
self.account_no = account_no
self._balance = balance
self.cond = threading.Condition()
# 定义代表是否已经存钱的旗标
self._flag = False
# 因为账户余额不允许随便修改,所以只为self._balance提供getter方法
def getBalance(self):
return self._balance
# 提供一个线程安全的draw()方法来完成取钱操作
def draw(self, draw_amount):
# 加锁,相当于调用Condition绑定的Lock的acquire()
self.cond.acquire()
try:
# 如果self._flag为假,表明账户中还没有人存钱进去,取钱方法阻塞
if not self._flag:
self.cond.wait()
else:
# 执行取钱操作
print(threading.current_thread().name
+ " 取钱:" + str(draw_amount))
self._balance -= draw_amount
print("账户余额为:" + str(self._balance))
# 将标识账户是否已有存款的旗标设为False
self._flag = False
# 唤醒其他线程
self.cond.notify_all()
# 使用finally块来释放锁
finally:
self.cond.release()
def deposit(self, deposit_amount):
# 加锁,相当于调用Condition绑定的Lock的acquire()
self.cond.acquire()
try:
# 如果self._flag为真,表明账户中已有人存钱进去,存钱方法阻塞
if self._flag:
self.cond.wait()
else:
# 执行存款操作
print(threading.current_thread().name\
+ " 存款:" + str(deposit_amount))
self._balance += deposit_amount
print("账户余额为:" + str(self._balance))
# 将表示账户是否已有存款的旗标设为True
self._flag = True
# 唤醒其他线程
self.cond.notify_all()
# 使用finally块来释放锁
finally:
self.cond.release()
import threading
import Account
# 定义一个函数,模拟重复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.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()
程序阻塞并不是死锁,死锁是等待其他线程释放同步监视器
Queue模块提供了三中阻塞队列,这些队列主要用于实现线程通信:
这三种队列的属性和函数基本相同:
import queue
# 定义一个长度为2的阻塞队列
bq = queue.Queue(2)
bq.put("Python")
bq.put("Python")
print("1111111111")
bq.put("Python")
print("2222222222")
import threading
import time
import queue
def product(bq):
str_tuple = ("Python", "Kotlin", "Swift")
for i in range(99999):
print(threading.current_thread().name + "生产者准备生产元组元素!")
time.sleep(0.2);
# 尝试放入元素,如果队列已满,则线程被阻塞
bq.put(str_tuple[i % 3])
print(threading.current_thread().name \
+ "生产者生产元组元素完成!")
def consume(bq):
while True:
print(threading.current_thread().name + "消费者准备消费元组元素!")
time.sleep(0.2)
# 尝试取出元素,如果队列已空,则线程被阻塞
t = bq.get()
print(threading.current_thread().name \
+ "消费者消费[ %s ]元素完成!" % t)
# 创建一个容量为1的Queue
bq = queue.Queue(maxsize=1)
# 启动3个生产者线程
threading.Thread(target=product, args=(bq, )).start()
threading.Thread(target=product, args=(bq, )).start()
threading.Thread(target=product, args=(bq, )).start()
# 启动一个消费者线程
threading.Thread(target=consume, args=(bq, )).start()
Event是一种简单的通信机制,就是一个线程发出一个Event,这个Event触发另一个线程,它提供了如下常用方法:
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() # 将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()
import threading
import Account
# 定义一个函数,模拟重复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.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()