一、概述-线程同步
如果两个线程的运行顺序不同,他有可能产生不同的结果,或者造成执行的轨迹或行为不相同,这时我们就需要使用到多线程的同步。
二、Lock
lock = threading.Lock() 创建锁
lock.acquire() 获得锁
lock.release() 释放锁,解锁
import time
import threading
# 生成一个锁对象
lock = threading.Lock()
num = 50
ts = []
def func():
global num # 全局变量
lock.acquire() # 获得锁,加锁
num1 = num
time.sleep(0.1)
num = num1 - 1
lock.release() # 释放锁,解锁
for i in range(50): # 开启50个线程
t = threading.Thread(target=func, args=())
t.start()
ts.append(t)
# 等待线程运行结束
for i in ts:
i.join()
print(num)
分析:
通过获得锁使线程挂起一直执行直到被释放。获取锁和释放锁目的是为了使线程一个个顺序执行
有获取释放锁 | 无获取释放所 | |
输出 | 0 | 99 |
原因是没有时所有线程执行顺序不确定
注:没有join输出为100,具体原因参考上一篇
三、Rlock-递归锁
递归锁能够让线程多次被获取和释放
Rlock = threading.RLock()
def func():
global num # 全局变量
Rlock.acquire() # 获得锁,加锁
num1 = num
time.sleep(0.1)
num = num1 - 1
Rlock.release() # 释放锁,解锁
'''Rlock.acquire() # 获得锁,加锁
num1 = num
time.sleep(0.1)
num = num1 - 1
Rlock.release() # 释放锁,解锁'''
num = 50
l = []
for i in range(50): # 开启50个线程
t = threading.Thread(target=func, args=())
t.start()
l.append(t)
# 等待线程运行结束
for i in l:
i.join()
print(num)
仅获取一次:输出为0
import time
import threading
# 生成一个锁对象
Rlock = threading.RLock()
def func():
global num # 全局变量
Rlock.acquire() # 获得锁,加锁
num1 = num
time.sleep(0.1)
num = num1 - 1
Rlock.release() # 释放锁,解锁
Rlock.acquire() # 获得锁,加锁
num1 = num
time.sleep(0.1)
num = num1 - 1
Rlock.release() # 释放锁,解锁
num = 50
l = []
for i in range(50): # 开启50个线程
t = threading.Thread(target=func, args=())
t.start()
l.append(t)
# 等待线程运行结束
for i in l:
i.join()
print(num)
获取释放两次:输出-50
四、Event事件锁S
event.isSet() | 返回event状态:True或者False |
| |
| |
| |
用于线程间相互通信,通过判断其他线程的状态(即event状态,用wait将自己挂起),直到event被set才开始执行
#event
import threading
event = threading.Event()
def func():
print('等待开始')
print(event.isSet())#此时event的状态
event.wait() # 等待事件发生
print('开始')
print(event.isSet())#此时event的状态
def connect():
print('连接')
event.set()
t1 = threading.Thread(target=func, args=())
t2 = threading.Thread(target=connect, args=())
t1.start()
t2.start()
输出:
等待开始
False
连接
开始
True
五、Queue-队列
队列(queue)一般会被用在生产者和消费者模型上。有了队列就无须进行加锁或者等待,Queue相当于缓冲区,生产者一个劲往里面扔东西,消费者想什么时候拿就什么时候拿。队列会通过先进先出或者先进 后出的模式,保证了单个数据不会进行同时被多个线程进行访问。
生产者消费者模型:
为什么要使用生产者和消费者模式
在python线程中,生产者就是生产数据的线程,消费者就是消费数据的线程。在多线程开发当中,如果生产者处理速度很快,而消费者处理速度很慢,那么生产者就必须等待消费者处理完,才能继续生产数据。同样的道理,如果消费者的处理能力大于生产者,那么消费者就必须等待生产者。为了解决这个问题于是引入了生产者和消费者模式。
什么是生产者消费者模式
生产者消费者模式是通过一个容器来解决生产者和消费者的强耦合问题。生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,消费者不找生产者要数据,而是直接从阻塞队列里取,阻塞队列就相当于一个缓冲区,平衡了生产者和消费者的处理能力。
队列的函数:
qsize() 队列大大致大小,非准确值
empty() 当前是否为空
full() 当前是否已满
put(item, block=True, timeout=None) 将item放入队列。
block=True, timeout=None 在必要时阻塞,直到有空位可用,timeout 为阻止的时间,超时抛出Full异常。
block=False 立即将item放入队列,队列已满引发Full异常。
put_nowait(item) 立即放入队列,同put(item,False)
get(block=True, timeout=None) 从队列中删除并返回一个item。
block=True, timeout=None 在必要时阻塞,直到有可用数据为止,timeout 为阻止的时间,超时抛出Empty异常。
block=False 立即获取队列中的可用数据,否则抛出Empty异常。
get_nowait() 立即获取队列中的数据,同get(False)。
task_done() 向已完成的队列任务发送一个信号。一般是告诉join() 我以完成任务。
join() 阻塞线程,直到队列为空才放行。
注意:join() 与 task_done() 是套组合拳,有使用 join() 必须在任务结束后执行 task_done() 通知队列。
import threading
import queue
import time
#创建一个队列,1代表maxSize
q=queue.Queue(1) #创建一个先进先出的队列
#q=queue.LifoQueue #创建一个后进先出的队列
#q=queue.PriorityQueue #优先级的队列
#定义生产者线程
class Producer(threading.Thread):
def run(self):
global q
count=0
while True:
time.sleep(5)
print("生产线程开始生产数据")
count+=1
msg="产品{}".format(count)
q.put(msg) #默认阻塞
print(msg)
#定义消费者线程
class Consumer(threading.Thread):
def run(self):
global q
while True:
print('消费者线程开始消费线程了')
msg=q.get() #默认阻塞
print('消费线程得到了数据:{}'.format(msg))
time.sleep(2)
if __name__ == '__main__':
t1=Producer()
t2=Consumer()
t1.start()
t2.start()
输出:
消费者线程开始消费线程了
生产线程开始生产数据
产品1消费线程得到了数据:产品1
消费者线程开始消费线程了
生产线程开始生产数据
产品2消费线程得到了数据:产品2
消费者线程开始消费线程了
生产线程开始生产数据
产品3消费线程得到了数据:产品3
消费者线程开始消费线程了
生产线程开始生产数据
产品4消费线程得到了数据:产品4
q.put(msg) :默认阻塞,如果队列中数据已经满了,会阻塞(进入等待状态); 参数:block,默认为True,表示数据已经进入等待,设为False表示不等待,如果队列中没有空位置,直接抛出异常 参数:timeout:设置等待的时长,如果超过时间还没有空位置,报异常 |
q.get() 队列中获取数据,如果队列为空会阻塞 参数:block,默认为True,表示数据已经进入等待,设为False表示不等待,如果队列中没有数据,直接抛出异常 参数:timeout:设置等待的时长,如果超过时间还没有空位置,报异常 |
q=queue.Queue() 创建一个先进先出的队列 q=queue.LifoQueue 创建一个后进先出的队列 q=queue.PriorityQueue 优先级的队列 q.qsize():获取队列的长度 |