【python内功修炼008】:信号量Semaphore和Event事件

一、信号量介绍


1.1 什么是信号量

信号量可以理解为多把锁,同时允许多个线程来更改数据。而互斥锁同时只允许一个线程更改数据。

1.2 信号量的作用

信号量的使用主要是用来保护共享资源,使得资源在一个时刻只有一个进程(线程)所拥有。

信号量的主要用途是用来控制线程的并发量的,Semaphore管理一个内置的计数器,每调用一次acquire()方法时,计数器-1,每调用一次release()方法时,内部计数器+1。

不过需要注意的是,Semaphore内部的计数器不能小于0!当它内部的计数器等于0的时候,这个线程会被锁定,进入阻塞状态,直到其他线程去调用release方法。

1.3 信号量和互斥锁的区别

信号量是用在多线程多任务 同步 的,互斥锁是用在多线程多任务 **互斥 ** 的

  • 信号量可以做到一个线程完成了某个动作就通过信号量告诉别的线程,别的线程再进行某些动作。
  • 互斥锁是指一个线程使用某个资源通过对其加锁而使得其他线程无法访问,直到这个线程解锁,其他线程才可以继续访问。

二、信号量用法


2.1 信号量(Semaphore)语法

s=Semaphore(value)

创建一个新的信号量。value是计数器的初始值。如果省略value,将默认设置为1

2.2 Semaphore常用方法

s.acquire(blocking):获取信号量。

如果内部计数器大于0,此方法将把它的值减1。
然后立即返回。如果它的值为0,此方法将阻塞,直到另一个线程调用release()方法为止。
blocking参数的行为与Lock和RLock对象中描述的相同。

s.release():通过将内部计数器的值加1来释放一个信号量。
如果计数器为0,而且另一个线程正在等待,该线程讲被唤醒。
如果有多个线程正在等待,只能从它的acquire()的调用中返回一个,并且顺序不确定。

2.3 Semaphore代码实例

import time
from threading import current_thread, Semaphore, active_count, Thread

def run():
    semaphore.acquire()   # 获取信号量
    time.sleep(1)   # 每隔1秒弹出一次运行结果
    print('线程:%s ' % current_thread().getName())

    semaphore.release()    # 释放信号量

if __name__=='__main__':
    semaphore = Semaphore(5)   # 声明semaphore实例,每次允许5个线程同时运行
    for i in range(30):     # 定义30个线程
        t = Thread(target=run)
        t.start()
while active_count != 1:   # 作用相当于join()等待线程执行完毕,如果活跃数为1,则说明只有主线程
    pass
else:
    print("---所有线程都完成了")

三、Event介绍


3.1 Event诞生的背景

通俗的例子:

在红路灯交通系统中,汽车能根据红绿灯情况选择是否通行;将交通系统代入到程序中,红路灯交通系统则是一个程序任务,汽车和红路灯是运行这个程序的线程。如何让汽车(N个)线程,来根据红路灯(1个)线程的结果来运行或者停止任务呢?这就是event控制多线程方案

Python threading模块不同于其它语言之处在于它没有提供线程的终止方法,而且python的多线程设计本身也是不希望用户这么做,但是很多时候我们得到某个结果后为了节省不必要的资源 必须停止其他线程的工作。

其实如果如果想要实现这个功能很简单,我们可以自己定义一个全局变量,在每次任务循环中判断这个变量的状态,如果某个线程中得到结果,那么改变变量,其他线程得知这个变量改变后break即可。

四、Event的用法


Event对象实现了简单的线程通信机制,它提供了设置信号,清除信号,等待等用于实现线程间的通信。

事件处理的机制:全局定义了一个“Flag”,如果“Flag”值为False,那么当程序执行event.wait方法时就会阻塞,如果“Flag”值为True,那么event.wait方法时便不再阻塞。clear:将“Flag”设置为Falseset:将“Flag”设置为True

4.1 Event对象方法

方法 作用 功能
set() 设置信号 默认设置Event对象内部的信号标志为真。Event对象提供了isSet()方法来判断其内部信号标志的状态,当使用event对象的set()方法后,isSet()方法返回真.
clear() 清除信号 可以清除Event对象内部的信号标志,即将其设为假,当使用Event的clear方法后,isSet()方法返回假
wait(timeout=None) 阻塞信号 Event对象wait的方法只有在内部信号为真的时候才会很快的执行并完成返回。当Event对象的内部信号标志位假时,则wait方法一直等待到其为真时才返回。
is_set() 返回信号 该方法返回 Event 的内部旗标是否为True

4.2 Event代码实例

'''

    模拟红路灯

    python线程的事件用于主线程控制其他线程的执行,事件主要提供了三个方法
set、wait、clear。

事件处理的机制:全局定义了一个“Flag”,如果“Flag”值为
False,那么当程序执行
event.wait
方法时就会阻塞,如果“Flag”值为True,那么event.wait
方法时便不再阻塞。

clear:将“Flag”设置为False
set:将“Flag”设置为True
'''

# is_set():该方法返回 Event 的内部旗标是否为True。
# set():该方法将会把 Event 的内部旗标设置为 True,并唤醒所有处于等待状态的线程。
# clear():该方法将 Event 的内部旗标设置为 False,通常接下来会调用 wait() 方法来阻塞当前线程。
# wait(timeout=None):该方法会阻塞当前线程。

from threading import Thread,current_thread, Event
import time, random

def Car(e):
    while True:
        if not e.is_set():  # Flase
            print('\033[31m红灯亮\033[0m,car--%s等着' % current_thread().getName())
            e.wait()  # 第一次 阻塞
            print('\033[32m车--%s 看见绿灯亮了\033[0m' % current_thread().getName())
            time.sleep(random.randint(3, 6))
            if not e.is_set():
                print(e.is_set())
                continue
            print('走你,car--%s' % current_thread().getName())
            break


def traffic_lights(e, inverval):
    while True:
        time.sleep(inverval)
        if e.is_set():  # 判断e的状态
            e.clear()  # e.is_set() ---->False
        else:
            e.set()   # Event 的内部旗标设置为 True


if __name__ == '__main__':
    e = Event()
    for i in range(3):
        p = Thread(target=Car, args=(e,))
        p.start()

    t = Thread(target=traffic_lights, args=(e, 10))
    t.start()

    print('============》')


结果:

红灯亮,car--Thread-1等着
红灯亮,car--Thread-2等着
红灯亮,car--Thread-3等着
============》
车--Thread-1 看见绿灯亮了车--Thread-2 看见绿灯亮了车--Thread-3 看见绿灯亮了


走你,car--Thread-3走你,car--Thread-2

走你,car--Thread-1
发布了72 篇原创文章 · 获赞 79 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/weixin_42444693/article/details/105283388