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

      • 信号量
        • 进程信号量
        • 线程信号量
      • 事件
        • 线程事件
        • 进程事件

信号量

进程信号量

互斥锁 同时只允许一个线程更改数据,而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

你可能感兴趣的:(Python基础)