条件(Condition)
使线程等待,当满意某条件时,才出狱n个线程。
Python提供的Condition对象提供了对复杂线程同步难题的支撑。
Condition被叫做条件变量,除了提供与Lock类似的acquire和release方法外,还提供了wait和notify方法。
线程首先acquire贰个口径变量,然后判别一些尺度。
假如条件不满意则wait;
借使基准满意,实香港行政局地管理改变规则后,通过notify方法公告任何线程,
任何处于wait状态的线程接到通报后会重新推断标准。
源源不断的重新那一进度,从而消除复杂的一块儿难点。
例:线程设置条件
import threading
def run(n):
con.acquire()
con.wait()
print("run the thread: %s" % n)
con.release()
if __name__ == '__main__':
con = threading.Condition()
for i in range(10):
t = threading.Thread(target=run, args=(i,))
t.start()
while True:
inp = input('>>>')
if inp == 'q':
break
con.acquire()
con.notify(int(inp))
con.release()
print('****')
运行如下:
>>>1
****
>>>run the thread: 0
2
****
>>>run the thread: 1
run the thread: 2
q
例2:通过条件变量控制线程分批执行
from threading import Condition, Thread
def func(i, con):
con.acquire()
con.wait()
print(i * '*')
con.release()
con = Condition()
# 定义了范围从1到9
for i in range(1, 10):
Thread(target=func, args=(i, con)).start()
while True:
# 分批控制线程执行
n = int(input('>>>'))
if n == 0:
break
con.acquire()
con.notify(n)
con.release()
运行效果如下:
>>>1
>>>*
2
>>>**
***
3
>>>******
****
*****
4
>>>*******
********
*********
0
以上,输入1则运行一次任务,1+2+3+4 10次己超出任务次数,完成后不会重复执行
例:那朵花躲迷藏
#!/usr/bin/env python
# _*_ coding: utf-8 _*_
# ____ Condition 条件变量
# ____ 模拟那朵花中的面码捉迷藏对话
import threading, time
# 将躲迷藏中搜寻的角色这个类创造出来,且继承了线程Thread这个类
class Seeker(threading.Thread):
def __init__(self, cond, name):
super(Seeker, self).__init__()
self.cond = cond
self.name = name
def run(self):
# 睡一秒,让躲猫猫的面码也运行起来,不然会阻塞住
time.sleep(1)
self.cond.acquire()
print(self.name + ': (藏)好了吗?')
self.cond.notify()
self.cond.wait()
print(self.name + ': (藏)好了吗?~~')
self.cond.notify()
self.cond.wait()
print(self.name + ': 看到你了!面码!')
self.cond.notify()
self.cond.wait()
self.cond.release()
print(self.name + ": 谢谢你,面码~ ")
# 再来是躲迷藏的面码
class Hider(threading.Thread):
def __init__(self, cond, name):
super(Hider, self).__init__()
self.cond = cond
self.name = name
def run(self):
self.cond.acquire()
self.cond.wait()
# 释放对锁的占用,同时线程挂起来在这里,直到被notify
print(self.name + ": 还没好哦~")
self.cond.notify()
self.cond.wait()
print(self.name + ": (藏)好了哦~")
self.cond.notify()
self.cond.wait()
self.cond.notify()
self.cond.release()
print(self.name + ": 阿,被看到了~")
cond = threading.Condition()
seeker = Seeker(cond, "仁太")
hider = Hider(cond, "面码")
seeker.start()
hider.start()
运行效果:交替进行的对话
仁太: (藏)好了吗?
面码: 还没好哦~
仁太: (藏)好了吗?~~
面码: (藏)好了哦~
仁太: 看到你了!面码!
面码: 阿,被看到了~
仁太: 谢谢你,面码~
线程中的时域信号量
同进度的1致,Semaphore管理八个停放的计数器,
每当调用acquire()时内置计数器-1;调用release() 时内置计数器+壹;
计数器不能够小于0;当计数器为0时,acquire()将封堵线程直到别的线程调用release()。
实例:(同时只有5个线程可以获得semaphore,即可以限制最大连接数为5):
from threading import Thread,Semaphore
import threading
import time
def func():
sm.acquire()
print('%s get sm' %threading.current_thread().getName())
time.sleep(3)
sm.release()
if __name__ == '__main__':
sm=Semaphore(5)
for i in range(23):
t=Thread(target=func)
t.start()
与进程池是一心分化的概念,进度池Pool(四),最大不得不发出五个过程,
而且原原本本都只是那八个经过,不会发生新的,而实信号量是发生一堆线程/进度。
线程中的事件
同进度的同样,线程的八个关键个性是各种线程都以独自运作且状态不行预测。
若果程序中的别的线程须要经过剖断有个别线程的图景来规定自身下一步的操作,那时线程同步难题就能够变得可怜劳苦。
为了缓慢解决那个难题,大家要求运用threading库中的伊芙nt对象。
对象涵盖3个可由线程设置的实信号标识,它同意线程等待有个别事件的发生。
在起来情形下,伊芙nt对象中的实信号标识棉被服装置为假。
只要有线程等待2个伊芙nt对象,
而那么些伊夫nt对象的注明为假,那么这几个线程将会被一贯不通直至该标识为真。
3个线程若是将一个伊夫nt对象的非实信号标识设置为真,它将唤起全部等待这么些伊芙nt对象的线程。
如若二个线程等待3个曾经棉被服装置为确实伊夫nt对象,那么它将忽略那几个事件,
继续施行。
event.isSet():再次回到event的情事值;
event.wait():如若 event.isSet()==False将封堵线程;
event.set():
设置event的场合值为True,全体阻塞池的线程激活进入就绪状态,
等待操作系统调节;
event.clear():复苏event的图景值为False。
例如说,有三个专门的学问线程尝试链接MySQL,要在链接前保障MySQL服务平常才让那二个职业线程去老是MySQL服务器,
若果老是不成事,都会去尝尝再一次连接。可以使用threading.伊夫nt机制来和睦各类职业线程的三番五次操作。
例:模拟连接mysql数据库
import threading
import time,random
from threading import Thread,Event
def conn_mysql():
count=1
while not event.is_set():
if count > 3:
raise TimeoutError('链接超时')
print('<%s>第%s次尝试链接' % (threading.current_thread().getName(), count))
event.wait(0.5)
count+=1
print('<%s>链接成功' %threading.current_thread().getName())
def check_mysql():
print('\033[45m[%s]正在检查mysql\033[0m' % threading.current_thread().getName())
time.sleep(random.randint(2,4))
event.set()
if __name__ == '__main__':
event=Event()
conn1=Thread(target=conn_mysql)
conn2=Thread(target=conn_mysql)
check=Thread(target=check_mysql)
conn1.start()
conn2.start()
check.start()
例:模拟连接数据库2
import time
import random
from threading import Event, Thread
# 模拟连接数据库
def connect_db(e):
count = 1
while count < 4:
print('尝试第%s次检测连接' % count)
e.wait(0.5)
# 如果不传参数会一直等到事件为True为止
# 如果传参数 传一个时间参数
count += 1
if e.is_set():
print('连接成功')
break
else:
print('连接失败')
def check_conn(e):
'''检测数据库是否可以连接'''
time.sleep(random.randint(1, 2))
e.set()
e = Event()
Thread(target=check_conn, args=(e,)).start()
Thread(target=connect_db, args=(e,)).start()
# 当你要做一件事情是有前提(前置条件)的时候
# 你就先去处理前提(前置条件)的问题 —— 前提处理好了,把状态设置成True
# 来控制即将要做的事情可以开始了。
运行效果如下:
尝试第1次检测连接
尝试第2次检测连接
连接成功
最棒循环的例子
经文的劳动者与买主难点:假设有一堆生产者(Producer)和一堆消费者(Consumer)通过多少个市面来交互产品。
劳动者的”战术“是假设商号上剩下的出品轻易一千个,那么就生育九二十一个产品放到商城上;
而顾客的”攻略“是一旦市镇上剩余产品的数据多余96个,那么就成本1个产品。
用Condition化解劳动者与买主难点的代码如下:
#!/usr/bin/env python
# _*_ coding: utf-8 _*_
# ____消费者与生产者模型,多线程,条件变量,无限循环
import threading
import time
# 生产者
class Producer(threading.Thread):
def run(self):
global count
while True:
if con.acquire():
# 当产品在市场超过1000个时开始等候
if count > 1000:
con.wait()
# 少于1000个则开始生产100个产品投入市场
else:
count = count + 100
msg = self.name + ' produce 100, count=' + str(count)
print(msg)
con.notify()
con.release()
time.sleep(1)
# 消费者
class Consumer(threading.Thread):
def run(self):
global count
while True:
# 当市场少于200个产品时,消费者等候
if con.acquire():
if count < 200:
con.wait()
# 否则消费
else:
count = count - 30
msg = self.name + ' consume 30, count=' + str(count)
print(msg)
con.notify()
con.release()
time.sleep(1)
# 初始产品为100个
count = 100
con = threading.Condition()
def main():
# 两个生产者
for i in range(2):
p = Producer()
p.start()
# 五个消费者
for i in range(5):
c = Consumer()
c.start()
if __name__ == '__main__':
main()
无限循环的部分运行效果:
Thread-1 produce 100, count=200
Thread-2 produce 100, count=300
Thread-3 consume 30, count=270
Thread-4 consume 30, count=240
Thread-5 consume 30, count=210
Thread-6 consume 30, count=180
Thread-1 produce 100, count=280
Thread-3 consume 30, count=250
Thread-5 consume 30, count=220
Thread-2 produce 100, count=320
Thread-4 consume 30, count=290
Thread-6 consume 30, count=260
Thread-7 consume 30, count=230
Thread-1 produce 100, count=330
Thread-4 consume 30, count=300
Thread-2 produce 100, count=400
Thread-5 consume 30, count=370
Thread-3 consume 30, count=340
Thread-6 consume 30, count=310
Thread-7 consume 30, count=280
Thread-1 produce 100, count=380
Thread-5 consume 30, count=350
Thread-3 consume 30, count=320
Thread-4 consume 30, count=290
Thread-2 produce 100, count=390
Thread-6 consume 30, count=360
Thread-7 consume 30, count=330
Thread-1 produce 100, count=430
Thread-3 consume 30, count=400
Thread-2 produce 100, count=500
解析差异
- 信号量 semaphore 允许统有时刻n个线程实践这段代码
- 事件 event 有1在那之中间的事件来决定wait的作为且调节的是兼具的线程
- 规则 condition
有1个之中的条件来调节wait的一言一动,能够各个可能分批次的调整线程的走向
end
参考:
铁乐学python_Day42_线程-实信号量事件规范