1.信号量(锁)
信号量用来控制线程并发数的,BoundedSemaphore或Semaphore管理一个内置的计数器,每当调用acquire()时-1,调用release()时+1.
计数器不能小于0,当计数器为0时,acquire()将阻塞线程至同步锁定状态,直到其他线程调用release().
BoundedSemaphore和Semaphore的唯一区别在于前者将在调用release()时检查计数器的值是否超过了计数器的初始值,如果超过了将抛出一个异常
作用:数据库连接限制
问题:是否一起进入,一起出?
分析:并不是,内部的计数器可以控制,如果有一个线程完成任务,释放了锁,其他线程就可以进入
[root@node2 semaphore]# cat semaphore.py
#!/usr/local/python3/bin/python3
import threading,time
class MyThread(threading.Thread):
def run(self):
if semaphore.acquire():
print(self.name)
time.sleep(3)
semaphore.release()
if __name__=="__main__":
semaphore=threading.BoundedSemaphore(5) #创建信号量,5是控制线程数
thrs=[]
for i in range(100):
thrs.append(MyThread()) #同时启动100个线程执行run方法
for t in thrs:
t.start()
[root@node2 semaphore]# python3 semaphore.py
Thread-1 #每5个出现一次
...
Thread-100
将for i in range(23)
[root@node2 semaphore]# python3 semaphore.py
Thread-1
....
Thread-20
Thread-21
Thread-22
Thread-23 最后只出3个
2.条件变量同步(锁)
描述:线程之间是可以通信的,某个线程走到一定时候,等待另一个线程的通知后,才继续向下走有一类线程需要满足条件之后才能够继续执行,Python提供了threading.Condition对象条件变量线程的支持,它除了能提供RLock()或Lock()的方法外,还供了wait()、notify()、notifyAll()方法.lock_cnotallow=threading.Condition([Lock/Rlock]): 锁是可选选项,不传人锁,对象自动创建一个RLock().
- wait(): 条件不满足时调用,线程会释放锁并进入等待阻塞
- notify(): 条件创造后调用,通知等待池激活一个线程
- notifyAll(): 条件创造后调用,通知等待池激活所有线程
场景:生产者是做包子的人,消费者是吃包子的人,需要生产者与消费者一起行动,得有包子,吃的人才可以吃
分析:生产者和消费者开始行动时,可能两个都有机会取得锁,如果消费者先得到锁,它会先wait,释放锁,让生产者先开始,生产者完成后通知消费者,消费者再次申请锁再开始吃包子,
[root@node2 threading]# cat condition.py
#!/usr/local/python3/bin/python3
import threading,time
from random import randint
class Producer(threading.Thread):
def run(self):
global L
while True:
val=randint(0,100) #随机生成数,相当于创建一个包子
print('Producer',self.name,":Append"+str(val),L)
if lock_con.acquire(): #请求一把锁
L.append(val) #向L中放包子
lock_con.notify() #通知消费者可以开始吃包子,相当于满足上一行代码的条件,可以激活消费者的锁
lock_con.release()
time.sleep(3)
class Consumer(threading.Thread):
def run(self):
global L #类似于承包子的东西
while True:
lock_con.acquire() #有可能consumer先获取锁,但是消费者只是吃包子,只能等待包子先做出来
if len(L)==0:
lock_con.wait() #wait先释放锁,然后一直阻塞,直到有人通知才向下走,但是程序不会从阻塞这里开始走,从lock_con.acquire()开始
print('consumer',self.name,":Delete"+str(L[0]),L)
del L[0]
lock_con.release()
time.sleep(0.25)
if __name__=="__main__":
L=[] #空列表
lock_con=threading.Condition()
threads=[]
for i in range(5): #启动5个线程来执行生产者的方法,相当于有5个人做包子
threads.append(Producer())
threads.append(Consumer()) #消费者对象
for t in threads: #开启了6个线程对象
t.start()
for t in threads:
t.join()
[root@node2 threading]# python3 condition.py
Producer Thread-1 :Append7 []
Producer Thread-2 :Append85 [7]
Producer Thread-3 :Append33 [7, 85]
Producer Thread-4 :Append1 [7, 85, 33]
Producer Thread-5 :Append2 [7, 85, 33, 1]
consumer Thread-6 :Delete7 [7, 85, 33, 1, 2]
consumer Thread-6 :Delete85 [85, 33, 1, 2]
consumer Thread-6 :Delete33 [33, 1, 2]
consumer Thread-6 :Delete1 [1, 2]
consumer Thread-6 :Delete2 [2]
3.同步条件event
描述:类似于条件,但是它不需要使用锁,内部有一个标志位,如果调用event.wait(),如果判断为False,就会把线程阻塞住,True就继续向下走,通过event.set函数来修改标志位为True,event.clear()修改标志位为False,使用isSet()返回是True还是False
条件同步和条件变量同步差不多,只是少了锁功能,因为条件同步设计于不访问共享资源的条件环境。event=threading.Event():条件环境对象,初始值为False:
- event.isSet(): 返回event的状态值
- event.wait(): 如果event.isSet()==False将阻塞线程
- event.set(): 设置event的状态值为True,所有阻塞池的线程激活进入就绪状态,等待操作系统调度
- event.clear(): 恢复event的状态值为False
场景:老板说晚上加班,工作说累,但是工人要在老板后面反应
[root@node2 threading]# cat ot.py
#!/usr/local/python3/bin/python3
import threading,time
class Boss(threading.Thread):
def run(self):
print("Boss: we gonna ot tonight util 22:00.") #打印加班
event.isSet() or event.set() #event.isSet返回状态值时还是Fasle,event.set()设置为True,这时worker也会相应执行
time.sleep(5) #此时标志位为True
print("Boss: <22:00>knock off.")
event.isSet() or event.set()
class Worker(threading.Thread):
def run(self):
event.wait() #默认是false,如果worker先得到资源,要wait,因为这是老板要说的话,老板说了后,才说累
print("Worker: tired!")
time.sleep(0.25)
event.clear() #相当于工作一段时间后,再把标志位设置为false,当boss去取时为False
event.wait()
print("Worker: oh...")
if __name__=="__main__":
event=threading.Event() #event取得事件
threads=[]
for i in range(5): #启动5个worker,一个boss
threads.append(Worker())
threads.append(Boss())
for t in threads:
t.start()
for t in threads:
t.join()
[root@node2 threading]# python3 ot.py
Boss: we gonna ot tonight util 22:00.
Worker: tired!
Worker: tired!
Worker: tired!
Worker: tired!
Worker: tired!
Boss: <22:00>knock off.
Worker: oh...
Worker: oh...
Worker: oh...
Worker: oh...
Worker: oh...
4.队列queue***(数据结构)
quue is especially userful in threaded programming when information must be exchaged safely between multiple threads
(1)、queue定义了3种信息列队模式类
Queus([maxsize]): FIFO列队模式(first in first out),[maxsize]定义列队容量,缺省为0,即无穷大
LifoQueue([maxsize]): LIFO列队模式(last in first out),类似于放糖到瓶子,先放入的只能后出,因为最后放的比较接近瓶盖
PriorityQueue([maxsize]): 优先级列队模式,使用此列队时,项目应该是(priority,data)的形式
(2)、queue队列类的方法
q.put(item[,block,[timeout]]): 将item放入队列,如果block设置为True(默认)时,列队满了,调用者将被阻塞,否则抛出Full异常,timeout设置阻塞超时
q.get([block,[timeout]]): 从列队中取出一个项目
q.qsize():返回列队的大小
q.full(): 如果列队满了返回True,否则为False
q.empty(): 如果列队为空返回True,否则为False
(3)、创建一个队列
[root@node2 threading]# cat q.py
#!/usr/local/python3/bin/python3
import queue
d=queue.Queue() #队列对象
d.put('reid') #插入数据
d.put('betty')
d.put('linux')
print(d.get()) #取数据
print(d.get())
print(d.get())
[root@node2 threading]# python3 q.py #先进先出
reid
betty
linux
报错:
[root@node2 threading]# python3 queue.py 脚本名与模块冲突
Traceback (most recent call last):
File "queue.py", line 2, in <module>
import queue
File "/root/py/threading/queue.py", line 3, in <module>
d=queue.Queue()
AttributeError: module 'queue' has no attribute 'Queue'
[root@node2 threading]# mv queue.py q.py 修改脚本名称
(4)、控制队列的量(队列满的情况)
[root@node2 threading]# cat q.py
#!/usr/local/python3/bin/python3
import queue
d=queue.Queue(2) #2是插入数据的量,如果插入的数量比这个大会造成阻塞,0代表是无限大
d.put('reid')
d.put('betty')
d.put('linux',0) #加入参数返回队列情况,0通知队列满了,1默认阻塞
print(d.get())
print(d.get())
print(d.get())
[root@node2 threading]# python3 q.py
Traceback (most recent call last):
File "q.py", line 6, in <module>
d.put('linux',0)
File "/usr/local/python3/lib/python3.6/queue.py", line 130, in put
raise Full
queue.Full ##显示队列已经满了
(5)、取数据(取的量多于插入的量)
[root@node2 threading]# cat q.py
#!/usr/local/python3/bin/python3
import queue
d=queue.Queue(3)
d.put('reid')
d.put('betty')
d.put('linux',0)
print(d.get())
print(d.get())
print(d.get())
print(d.get(0)) ##0,没有数据时会返回结果
[root@node2 threading]# python3 q.py
reid
betty
linux
Traceback (most recent call last):
File "q.py", line 11, in <module>
print(d.get(0))
File "/usr/local/python3/lib/python3.6/queue.py", line 161, in get
raise Empty
queue.Empty ###等待插入数据
(6)、队列的作用
a. 适用于多线程
分析:如果在单线程情况,阻塞住就没办法,直接使用列表更方便,但是一到多线程就有问题,涉及线程安全,列表的数据,那个线程都可以取,如列表中有1,2,3,4个数,两个线程同时取4这个数,都可以同时取得,这时就无法控制
但是在队列中使用时,多线程去取数不会造成同时取得一个数,因为队列中本身就有一把锁,在数据结构上加了一把锁,因为以后会经常的操作数据,还要不断的加锁
使用列表时,线程不安全
[root@node2 threading]# cat thread-list.py #模拟队列,先取,再删除
#!/usr/local/python3/bin/python3
import threading,time
li=[1,2,3,4,5]
def pri():
while li:
a=li[-1] #取最后一个数
print(a)
time.sleep(1)
try:
li.remove(a)
except:
print('---',a)
t1=threading.Thread(target=pri,args=()) #两个线程同时去取,可能同时取得同一个数,造成线程不安全
t1.start()
t2=threading.Thread(target=pri,args=())
t2.start()
[root@node2 threading]# python3 thread-list.py
5 ##同时取得两个5
5
4
--- 5
4
3
--- 4
3
2
--- 3
2
1
--- 2
1
--- 1
实例1:三个生产者一个消费者,在有队列的情况下有一把锁 ,三个生产者不会造乱,保证数据安全
[root@node2 threading]# cat qinstance.py
#!/usr/local/python3/bin/python3
import threading,queue
from time import sleep
from random import randint
class Production(threading.Thread):
def run(self):
while True:
r=randint(0,100)
q.put(r) ##生产一个包子,有三个Production对象向里面生产包子,如果没有锁就会有问题,最大限制保证数据安全
print("produce %s baozi"%r)
sleep(1)
class Proces(threading.Thread):
def run(self):
while True:
re=q.get() #消费者一直吃包子
print("eat %s baozi"%re)
if __name__=="__main__":
q=queue.Queue(10)
threads=[Production(),Production(),Production(),Proces()] #创建4个线程对象,前三个是生产者,最后一个是消费者
for t in threads:
t.start()
[root@node2 threading]# python3 qinstance.py
produce 100 baozi
produce 80 baozi
produce 62 baozi
eat 100 baozi
eat 80 baozi
eat 62 baozi
实例2
[root@node2 threading]# cat qinstance1.py
#!/usr/local/python3/bin/python3
import time,random
import queue,threading
q = queue.Queue()
def Producer(name):
count=0
while count<20:
time.sleep(random.randrange(3))
q.put(count)
print('Producer %s has produced %s baozi..' %(name,count))
count +=1
def Consumer(name):
count=0
while count<20:
time.sleep(random.randrange(4))
if not q.empty(): #不为空时
data = q.get()
print(data)
print('\033[32;1mConsumer %s has eat %s baozi...\033[0m' %(name,data))
else:
print("------no baozi anymore ------")
count +=1
p1 = threading.Thread(target=Producer,args=('A',))
c1 = threading.Thread(target=Consumer,args=('B',))
p1.start()
c1.start()
[root@node2 threading]# python3 qinstance1.py
Producer A has produced 0 baozi..
Producer A has produced 1 baozi..
Producer A has produced 2 baozi..
0
Consumer B has eat 0 baozi...
Producer A has produced 3 baozi..
Producer A has produced 4 baozi..
1
Consumer B has eat 1 baozi...
2
Consumer B has eat 2 baozi...
3
Consumer B has eat 3 baozi...
4
Consumer B has eat 4 baozi...
Producer A has produced 5 baozi..
Producer A has produced 6 baozi..
5
Consumer B has eat 5 baozi...