python--线程同步原语
threading模块是python3里面的多线程模块,模块内集成了许多的类,其中包括thread,condition,event,lock,rlock,semaphore,timer等等。下面这篇文章主要通过案例来说明其中的event和segmaphore(boundedsegmaphore)的使用。关于lock的使用可以移步到我之前写的文章
event
event类内部保存着一个flags参数,标志事件的等待与否。
event类实例函数
1. set() 将flags设置为true,事件停止阻塞
2. clear() 将flags重新设置为false,删除flags,事件重新阻塞
3. wait() 将事件设置为等待状态
4.is_set()判断flags是否被设置,如果被设置返回true,否则返回false
(1)单个事件等待其他事件的发生
具体代码:
from time import ctime,sleep event = event() def event_wait(): print(ctime()) event.wait() print('这是event_wait方法中的时间',ctime()) def event_set(n): sleep(n) event.set() print('这是event_set方法中的时间', ctime()) thread1 = thread(target=event_wait) thread2 = thread(target=event_set,args=(3,)) thread1.start() thread2.start()
结果:
sat nov 17 10:01:05 2018 这是event_wait方法中的时间 sat nov 17 10:01:08 2018 这是event_set方法中的时间 sat nov 17 10:01:08 2018
(2)多个事件先后发生
下面以赛跑来作为例子。假设5条跑道上,每条跑道各有一名运动员,分别为abcde。
具体代码:
from threading import event from threading import thread import threading event = event() def do_wait(athlete): racetrack = threading.current_thread().getname() print('%s准备就绪' % racetrack) event.wait() print('%s听到枪声,起跑!'%athlete) thread1 = thread(target=do_wait,args=("a",)) thread2 = thread(target=do_wait,args=("b",)) thread3 = thread(target=do_wait,args=("c",)) thread4 = thread(target=do_wait,args=("d",)) thread5 = thread(target=do_wait,args=("e",)) threads = [] threads.append(thread1) threads.append(thread2) threads.append(thread3) threads.append(thread4) threads.append(thread5) for th in threads: th.start() event.set() #这个set()方法很关键,同时对5个线程中的event进行set操作
结果:
thread-1准备就绪 thread-2准备就绪 thread-3准备就绪 thread-4准备就绪 thread-5准备就绪 e听到枪声,起跑! a听到枪声,起跑! b听到枪声,起跑! d听到枪声,起跑! c听到枪声,起跑!
可以看出多个线程中event的set()是随机的,其内部的实现是因为一个notify_all()方法。这个方法会一次性释放所有锁住的事件,哪个线程先抢到线程运行的时间片,就先释放锁。
之所以能够只调用一个set()函数就可以实现所有event的退出阻塞,是因为event.wait()是在线程内部实现的,而set()函数是在进程中调用,python多线程共享一个进程内存空间。如果是在不同进程中调用这两个函数则无法实现。
boundedsegmaphore
如果在主机执行io密集型任务的时候再执行这种短时间内完成大量任务(多线程)的程序时,计算机就有很大可能会宕机。
这时候就可以为这段程序添加一个计数器(counter)功能,来限制一个时间点内的线程数量。当每次进行io操作时,都需要向segmaphore请求资源(锁),如果没有请求到,就阻塞等待,请求成功才就像执行任务。
boundedsegmaphore和segmaphore的区别
boundedsegmaphore请求的锁数量固定为传入参数,而segmaphore请求的锁数量可以超过传入的参数。
主要函数:
1. acquire() 请求锁
2. release() 释放锁
下面以一个租房的例子来说明这种固定锁数量的机制。假设一家小公寓有6间房,原本有2个住户在住着。
具体代码实现:
from threading import boundedsemaphore,lock,thread from time import sleep from random import randrange lock = lock() num = 6 hotel = boundedsemaphore(num) def logout(): lock.acquire() print('i want to logout') print('a customer logout...') try: hotel.release() print('welcome again') except valueerror: print('sorry,wait a moment.') lock.release() def login(): lock.acquire() print('i want to login') print('a customer login...') if hotel.acquire(false): print('ok,your room number is...') else: print('sorry,our hotel is full') lock.release() #房东 def producer(loops): for i in range(loops): logout() print('还剩%s' % hotel._value, '房间') sleep(randrange(2)) #租客 def consumer(loops): for i in range(loops): login() print('还剩%s' % hotel._value, '房间') sleep(randrange(2)) def main(): print('start') room_num = hotel._value print('the hotel is full with %s room'%room_num) #原本有2个住户 hotel.acquire() hotel.acquire() thread1 = thread(target=producer,args=(randrange(2,8),)) thread2 = thread(target=consumer,args=(randrange(2,8),)) thread1.start() thread2.start() if __name__ == '__main__': main()
结果:
the hotel is full with 6 room i want to logout a customer logout... welcome again 还剩5 房间 i want to logout a customer logout... welcome again 还剩6 房间 i want to login a customer login... ok,your room number is... 还剩5 房间 i want to login a customer login... ok,your room number is... 还剩4 房间
可以看出,房间数目永远不会超过6,因为_value值(boundedsegmaphore内部的计数器counter)一定是传入的参数6。