Python信号量、条件变量和事件

信号量

        信号量通过其内部的一个int变量来控制行为,默认情况下,threading.Semaphore创建的信号量,内部int的初始值为1,此时行为模式与一个lock互斥量没啥区别。

        多线程同时运行,能提高程序的运行效率,但是并非线程越多越好,而semaphore信号量可以通过内置计数器来控制同时运行线程的数量,启动线程(消耗信号量)内置计数器会自动减一,线程结束(释放信号量)内置计数器会自动加一;内置计数器为零,启动线程会阻塞,直到有本线程结束或者其他线程结束为止;

        在semaphore信号量有一个内置计数器,控制线程的数量,acquire()会消耗信号量,计数器会自动减一;release()会释放信号量,计数器会自动加一;当计数器为零时,acquire()调用被阻塞,直到release()释放信号量为止。

semaphore信号量使用:

创建多个线程,限制同一时间最多运行3个线程,示例代码如下:

import datetime
import threading
import time


def run():
    semaphore.acquire()
    print(datetime.datetime.now())
    time.sleep(2)
    semaphore.release()


if __name__ == '__main__':
    semaphore = threading.Semaphore(3)
    for i in range(30):
        t = threading.Thread(target=run)
        t.start()

输出结果:

        根据打印的日志可以看出,同一时间只有3个线程运行,间隔两秒之后,再次启动3个线程,直到30个线程全部运行结束为止;如果没有设置信号量Semapaore,创建线程直接start(),输出的时间全部都是一样的。

条件变量

一.线程条件变量Condition相关函数介绍

acquire() — 线程锁,注意线程条件变量Condition中的所有相关函数使用必须在acquire() /release() 内部操作;

release() — 释放锁,注意线程条件变量Condition中的所有相关函数使用必须在acquire() /release() 内部操作;

wait(timeout) — 线程挂起(阻塞状态),直到收到一个notify通知或者超时才会被唤醒继续运行(超时参数默认不设置,可选填,类型是浮点数,单位是秒)。wait()必须在已获得Lock前提下才能调用,否则会触发RuntimeError;

notify(n=1) — 通知其他线程,那些挂起的线程接到这个通知之后会开始运行,缺省参数,默认是通知一个正等待通知的线程,最多则唤醒n个等待的线程。notify()必须在已获得Lock前提下才能调用,否则会触发RuntimeError,notify()不会主动释放Lock;

notifyAll() — 如果wait状态线程比较多,notifyAll的作用就是通知所有线程;

        Condition自身提供了wait/notify/notifyAll方法,用于阻塞/通知其他并行线程,可以访问共享资源了。可以这么理解,Condition提供了一种多线程通信机制,假如线程1需要数据,那么线程1就阻塞等待,这时线程2就去制造数据,线程2制造好数据后,通知线程1可以去取数据了,然后线程1去获取数据。

示例:(老师课堂提问)

import threading


class Mythread(threading.Thread):
    def __init__(self, func, name):
        super(Mythread, self).__init__(target=func, args=name)


def teacher(name):
    # 条件变量condition 线程上锁
    con.acquire()
    print(f"{name}:张三,你来回答一下这个问题")
    # 唤醒正在等待(wait)的线程
    con.notify()
    # 等待对方回应消息,使用wait阻塞线程,等待对方通过notify唤醒本线程
    con.wait()
    print(f"{name}:这也不会,那也不会,一天天上学都干嘛吃的")
    # 唤醒对方
    con.notify()
    # 等待消息答应
    con.wait()
    print(f"{name}:上课白给你们讲了,这一题简单再回答不上来就不应该了")
    # 唤醒对方
    con.notify()
    # 等待消息答应
    con.wait()
    print(f"{name}:嗯,还行,你先坐下吧,下次上课再不认真听就不会像这次这么轻易饶过你了!")
    # 唤醒对方
    con.notify()
    # 条件变量condition 线程释放锁
    con.release()


def student01(name):
    # 条件变量condition 线程上锁
    con.acquire()
    # wait阻塞状态,等待其他线程通过notify唤醒本线程
    con.wait()
    print(f"{name}:老师,这道题我不太会")
    # 唤醒对方
    con.notify()
    # 等待消息答应
    con.wait()
    print(f"{name}:我不会那也没办法啊,要不老师你来个简单点的...")
    # 唤醒对方
    con.notify()
    # 等待消息答应
    con.wait()
    print(f"{name}:嘿,这个我知道:这题选C")
    # 唤醒对方
    con.notify()

    con.release()


if __name__ == "__main__":
    # 创建条件变量condition
    con = threading.Condition()
    # 创建并初始化线程
    t1 = Mythread(teacher, "A")
    t2 = Mythread(student01, "B")
    # 启动线程 -- 注意线程启动顺序,启动顺序很重要
    t2.start()
    t1.start()

    # 阻塞主线程,等待子线程结束
    t1.join()
    t2.join()
    print("程序结束!")

输出结果:

 事件

一.python事件Event相关函数介绍

set() — 全局内置标志Flag,将标志Flag 设置为 True,通知在等待状态(wait)的线程恢复运行;

isSet() — 获取标志Flag当前状态,返回True 或者 False;

wait() — 一旦调用,线程将会处于阻塞状态,直到等待其他线程调用set()函数恢复运行;

clear() — 将标志设置为False;

        事件event中有一个全局内置标志Flag,值为 True 或者False。使用wait()函数的线程会处于阻塞状态,此时Flag指为False,直到有其他线程调用set()函数让全局标志Flag置为True,其阻塞的线程立刻恢复运行,还可以用isSet()函数检查当前的Flag状态.下面我们看一个例子:

具体案例:第一个线程中获取当前时间,判断当前时间3秒之后,触发“事件”对象。在另一个线程中,作为数学考试结束的判断变量,否则一直处于考试中,并打印。

import datetime
import threading
import time


def time01():
    now_time01 = datetime.datetime.now()
    print(now_time01)
    time.sleep(3)
    now_time02 = datetime.datetime.now()
    print(now_time02)
    event.set()


def mathematics_examination():
    while 1:
        print("正在进行数学考试")
        time.sleep(0.5)
        if event.is_set():
            break


class Mythread(threading.Thread):
    def __init__(self, func):
        super(Mythread, self).__init__(target=func)

if __name__ == '__main__':
    event = threading.Event()
    all_thread = []
    t01 = Mythread(time01)
    t02 = Mythread(mathematics_examination)
    all_thread.append(t01)
    all_thread.append(t02)
    for i in range(2):
        all_thread[i].start()

输出结果:

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

慵懒之龟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值