您的位置:首页 > 编程语言 > Python开发

python基础-信号量Semaphore(进程_线程)、事件Event(进程_线程)

2017-12-06 22:38 751 查看
信号量
进程信号量

线程信号量

事件
线程事件

进程事件

信号量

进程信号量

互斥锁 同时只允许一个线程更改数据,而Semaphore是同时允许一定数量的线程更改数据 ,比如厕所有3个坑,那最多只允许3个人上厕所,后面的人只能等里面有人出来了才能再进去,如果指定信号量为3,那么来一个人获得一把锁,计数加1,当计数等于3时,后面的人均需要等待。一旦释放,就有人可以获得一把锁

信号量与进程池的概念很像,但是要区分开,信号量涉及到加锁的概念


from multiprocessing import Process,Semaphore
import time,random

def go_wc(sem,user):
sem.acquire()
print('%s 占到一个茅坑' %user)
time.sleep(random.randint(0,3)) #模拟每个人拉屎速度不一样,0代表有的人蹲下就起来了
sem.release()

if __name__ == '__main__':
sem=Semaphore(3)
p_l=[]
for i in range(10):
p=Process(target=go_wc,args=(sem,'user%s' %i,))
p.start()
p_l.append(p)

for i in p_l:
i.join()
print('============》')


输出如下:

E:\python\python_sdk\python.exe E:/python/py_pro/python.py
user0 占到一个茅坑
user1 占到一个茅坑
user2 占到一个茅坑

user3 占到一个茅坑

user4 占到一个茅坑
user5 占到一个茅坑
user6 占到一个茅坑

user7 占到一个茅坑
user8 占到一个茅坑
user9 占到一个茅坑

============》

Process finished with exit code 0


线程信号量

同进程的一样

Semaphore管理一个内置的计数器,

每当调用acquire()时内置计数器-1;

调用release() 时内置计数器+1;

计数器不能小于0;当计数器为0时,acquire()将阻塞线程直到其他线程调用release()。

def func(sm):
sm.acquire()
print('%s get sm' %threading.current_thread().getName())

time.sleep(2)
sm.release()
if __name__ == '__main__':
sm=Semaphore(3)
for i in range(10):
t=Thread(target=func,args=(sm,))
t.start()


输出如下:

E:\python\python_sdk\python.exe E:/python/py_pro/python.py
Thread-1 get sm
Thread-2 get sm
Thread-3 get sm

Thread-4 get sm
Thread-5 get sm
Thread-6 get sm

Thread-7 get sm
Thread-9 get sm
Thread-8 get sm

Thread-10 get sm

Process finished with exit code 0


事件

线程事件

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

event.isSet():返回event的状态值;

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

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

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

我们来看一个例子:

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

from threading import Thread,Event
import threading
import time,random
def conn_mysql():
count=1
#初始状态false
while not event.is_set():
if count > 3:
raise TimeoutError('链接超时')
print('<%s>第%s次尝试链接' % (threading.current_thread().getName(), count))
event.wait(1)

count+=1

print('<%s>链接成功' %threading.current_thread().getName())

def check_mysql():
print('\033[45m[%s]正在检查mysql\033[0m' % threading.current_thread().getName())
time.sleep(1.5)
#设置event的状态值为True
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()


输出如下:

E:\python\python_sdk\python.exe E:/python/py_pro/python.py
<Thread-1>第1次尝试链接
<Thread-2>第1次尝试链接
[Thread-3]正在检查mysql
<Thread-1>第2次尝试链接
<Thread-2>第2次尝试链接
<Thread-2>链接成功
<Thread-1>链接成功

Process finished with exit code 0


上例的代码大概意思是:

开启3个线程,2个是链接操作,1个是监测链接,初始化event.is_set为false,链接线程链接(通过print说明),然后event.wait(1)将阻塞线程1秒,然后另外一个链接线程,同样的操作,同时运行的check线程,在1.5秒后,设置event的状态值为True,在2个链接线程进行第二次链接时候,就输出链接成功了

进程事件

# from threading import Thread,Event
# import threading
import time,random,os
from  multiprocessing import  Process,Event
def conn_mysql(event):
count=1
#初始状态false
while not event.is_set():
if count > 3:
raise TimeoutError('链接超时')
print('<%s>第%s次尝试链接' % (os.getpid(), count))
event.wait(1)

count+=1

print('<%s>链接成功' %os.getpid())

def check_mysql(event):
print('\033[45m[%s]正在检查mysql\033[0m' % os.getpid())
time.sleep(1.5)
#设置event的状态值为True
event.set()

if __name__ == '__main__':
event=Event()
conn1=Process(target=conn_mysql,args=(event,))
conn2=Process(target=conn_mysql,args=(event,))
check=Process(target=check_mysql,args=(event,))

conn1.start()
conn2.start()
check.start()


输出如下:

E:\python\python_sdk\python.exe E:/python/py_pro/python.py

<9328>第1次尝试链接

<6400>第1次尝试链接

[5648]正在检查mysql

<9328>第2次尝试链接

<6400>第2次尝试链接

<9328>链接成功

<6400>链接成功

Process finished with exit code 0
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: