Py004-01-16信号量和Event

信号量

信号量也是一把锁,可以指定信号量为5,对比互斥锁同一时间只能有一个任务抢到锁去执行,信号量同一时间可以有5个任务拿到锁去执行,如果说互斥锁是合租房屋的人去抢一个厕所,那么信号量就相当于一群路人争抢公共厕所,公共厕所有多个坑位,这意味着同一时间可以有多个人上公共厕所,但公共厕所容纳的人数是一定的,这便是信号量的大小

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from threading import Thread,Semaphore
import threading
import time

def func():
sm.acquire()
print('%s 占住了坑位' %threading.current_thread().getName())
time.sleep(3)
print('%s 解脱了-------------------\n' % threading.current_thread().getName())
sm.release()

if __name__ == '__main__':
# 公共厕所只有3个坑
sm=Semaphore(3)

# 23个人冲进厕所
for i in range(23):
t=Thread(target=func)
t.start()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from threading import Thread,Semaphore,currentThread
import time,random

# 公共厕所只有三个坑
sm=Semaphore(3)

def task():
with sm:
print('%s get 厕所' %currentThread().getName())
time.sleep(random.randint(1,3))
print('%s out 厕所----------\n' % currentThread().getName())


if __name__ == '__main__':
for i in range(10):
t=Thread(target=task)
t.start()

解析

1
2
3
4
Semaphore管理一个内置的计数器,
每当调用acquire()时内置计数器-1;
调用release() 时内置计数器+1;
计数器不能小于0;当计数器为0时,acquire()将阻塞线程直到其他线程调用release()。

Event

线程的一个关键特性是每个线程都是独立运行且状态不可预测。

如果程序中的其 他线程需要通过判断某个线程的状态来确定自己下一步的操作,这时线程同步问题就会变得非常棘手。为了解决这些问题,我们需要使用threading库中的Event对象。 对象包含一个可由线程设置的信号标志,它允许线程等待某些事件的发生。在 初始情况下,Event对象中的信号标志被设置为假。如果有线程等待一个Event对象, 而这个Event对象的标志为假,那么这个线程将会被一直阻塞直至该标志为真。一个线程如果将一个Event对象的信号标志设置为真,它将唤醒所有等待这个Event对象的线程。如果一个线程等待一个已经被设置为真的Event对象,那么它将忽略这个事件, 继续执行

1
2
3
4
5
6
7
8
9
from threading import Event

event.isSet():返回event的状态值;是否被触发

event.wait():如果 event.isSet()==False将阻塞线程;

event.set(): 设置event的状态值为True,所有阻塞池的线程激活进入就绪状态, 等待操作系统调度;

event.clear():恢复event的状态值为False。

上课实例

在高中以下,老师没宣布下课大部分人都会停在座位上。。。佯装上课。。。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
from threading import Thread,Event
import time

event=Event()

def student(name):
print('学生%s 正在听课----------' %name)
event.wait()
print('学生%s 下课并开始自由活动--吃鸡吃鸡!!!' %name)


def teacher(name):
print('老师%s 正在授课' %name)
time.sleep(7)
event.set()


if __name__ == '__main__':
stu1=Thread(target=student,args=('刘备',))
stu2=Thread(target=student,args=('关羽',))
stu3=Thread(target=student,args=('张飞',))
t1=Thread(target=teacher,args=('诸葛',))

stu1.start()
stu2.start()
stu3.start()
t1.start()

'''
学生调用 event.wait() 就会一直等待老师 宣布下课(调用event.set())
所以开始上课后 直到老师 通知下课(event.set())学生才被释放
'''

有木有学生不等老师的情况?

有,大学里 老师讲课学生不会等老师宣布,因为你会逃课

设置超时时间

如果20分钟内你不点名,我就走了。。。哈哈哈

1
2
3
4
5
# wait能接受参数,单位是秒——超时时间
def student(name):
print('学生%s 正在听课----------' %name)
event.wait(2)
print('学生%s 下课并开始自由活动--吃鸡吃鸡!!!' %name)

例如,有多个工作线程尝试链接MySQL,我们想要在链接前确保MySQL服务正常才让那些工作线程去连接MySQL服务器,如果连接不成功,都会去尝试重新连接。那么我们就可以采用threading.Event机制来协调各个工作线程的连接操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
from threading import Thread,Event,currentThread
import time

event=Event()

def conn():
n=0
while not event.is_set():
if n == 3:
print('%s try too many times' %currentThread().getName())
return
print('%s try %s' %(currentThread().getName(),n))
event.wait(0.5)
n+=1

print('%s is connected' %currentThread().getName())


def check():
print('%s is checking' %currentThread().getName())
time.sleep(5)
event.set()


if __name__ == '__main__':
for i in range(3):
t=Thread(target=conn)
t.start()
t=Thread(target=check)
t.start()

定时器

看看javascript的定时器

1
2
3
4
5
6
7
8
9
10
setTimeout(function(){
console.log('冲咖啡。。。')
},1000)
// 一秒后 执行该函数


setInterval(function(){
console.log('抢票。。。')
},5000)
// 每5秒 看一下出票信息

python 的定时器,指定n秒后执行某操作

1
2
Timer(延迟时间,执行的函数,args=?,kwargs=?) 
支持给 执行函数传递参数
1
2
3
4
5
6
7
from threading import Timer

def hello():
print("hello, world")

t = Timer(1, hello)
t.start() # after 1 seconds, "hello, world" will be printed