python多线程学习:信号量、条件变量和事件的使用

信号量Semaphore

# !/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2022/1/18 9:33
# @Author  : 波澜不惊
# @File    : 信号量Semaphore.py
# 信号量也是一把锁,用来控制线程并发数
# 信号量通过一个内置的计数器,当加锁时,该数-1,释放时该数+1
# 计数器不能小于0,当计数器为0时,加锁操作会让线程进入阻塞状态,
# 直到其他线程释放锁之后计数器有空位子
# BoundedSemaphore与Semaphore的唯一区别在于前者将调用release()时检查计数器的值是否超过了初始值
# 如果超过了将抛出一个异常

import threading
import time
from sys import stdout


class myThread(threading.Thread):
    def run(self):
        if semaphore.acquire():
            stdout.write(self.name+" "+time.ctime()+" \n")
            time.sleep(0.1)
            semaphore.release()


if __name__ == "__main__":
    semaphore = threading.Semaphore(2)
    threadLock = threading.Lock()
    list1 = []
    for i in range(10):
        list1.append(myThread())
    for i in list1:
        i.start()

执行结果

Thread-1 Thu Jan 20 23:32:43 2022 
Thread-2 Thu Jan 20 23:32:43 2022 
Thread-3 Thu Jan 20 23:32:43 2022 
Thread-4 Thu Jan 20 23:32:43 2022 
Thread-5 Thu Jan 20 23:32:43 2022 
Thread-6 Thu Jan 20 23:32:43 2022 
Thread-7 Thu Jan 20 23:32:43 2022 
Thread-8 Thu Jan 20 23:32:43 2022 
Thread-10 Thu Jan 20 23:32:43 2022 
Thread-9 Thu Jan 20 23:32:43 2022 

进程已结束,退出代码为 0

条件变量Condition

# !/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2022/1/18 9:34
# @Author  : 波澜不惊
# @File    : 条件变量Condition.py
# 条件变量就相当于一个加了条件判断的锁
# Condition通常与一个锁相关联
# 相关函数:
# acquire -线程锁,注意线程条件变量Condition中所有相关函数使用必须在acquire/release内部操作
# release -释放锁,同样,在acquire/release内部操作
# wait(timeout) -线程挂起(阻塞),直到一个notify通知或者超时才会被唤醒继续运行(超时参数默认不设置,可选填,浮点数,单位秒)
# wait必须在已经获得Lock的前提下调用,负责会触发RuntimeError
# notify(n=1) -通知其他线程,那些挂起的线程接到这个通知之后会开始运行,
# 缺省参数是通知一个正等待通知的线程,最多则唤醒n个等待的线程,
# 和wait一样,notify也要在已经获得Lock的前提下调用,否则触发RuntimeError
# 但不同的是,notify不会主动释放Lock
# notifyAll -所见即所得,通知所有线程
import ctypes
import random
import threading
import time


# 生产者与消费者例子
class Bread(object):
    def __init__(self, num):
        self.num = num


con = threading.Condition()  # Condition条件变量对象
bread = Bread(5)  # 创建对象方便全局使用,女朋友先倒了5杯放在桌子上


class Consumers(threading.Thread):
    def run(self):
        con.acquire()  # 上锁
        # con.wait()      # 等待女朋友倒酒
        while True:
            if bread.num == 0:
                print("淦!小看我的酒量吗?快倒酒!")
                con.notify()  # 通知女朋友启动
                con.wait()  # 你等着喝
            else:
                bread.num -= 1
                print("你喝下一杯,剩下" + str(bread.num) + "杯")
                time.sleep(1)


class Producer(threading.Thread):
    def run(self):
        con.acquire()
        while True:
            n = random.randint(2, 5)
            bread.num += n
            print("御弟哥哥,刚倒了" + str(n) + "杯,快喝吧")
            time.sleep(2)
            con.notify()
            con.wait()


consumers = Consumers()
producer = Producer()
consumers.start()
producer.start()

执行结果

你喝下一杯,剩下4杯
你喝下一杯,剩下3杯
你喝下一杯,剩下2杯
你喝下一杯,剩下1杯
你喝下一杯,剩下0杯
淦!小看我的酒量吗?快倒酒!
御弟哥哥,刚倒了3杯,快喝吧
你喝下一杯,剩下2杯
你喝下一杯,剩下1杯
你喝下一杯,剩下0杯
淦!小看我的酒量吗?快倒酒!
御弟哥哥,刚倒了5杯,快喝吧
你喝下一杯,剩下4杯
你喝下一杯,剩下3......

事件Event

举了个放学回家的例子,挺合适的

# !/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2022/1/18 9:35
# @Author  : 波澜不惊
# @File    : 事件Event.py
# 事件对象管理一个内部标志,通过set()方法将其设置为True,
# 使用clear()方法设置为False,wait()方法阻塞,直到标志为True
# 该标志缺省值为False
# is_set -当且仅当内部标志为True时返回True
# set -将内部标志设置为True,所有等待它称为True的线程都被唤醒,当标志为True时,wait是不会阻塞的
# clear -将内部标志重置为False,然后调用wait的线程将会阻塞
# wait -阻塞直到内部标志为True,如果调用wait时内部标志为True,则立即返回True
# wait方法总是返回True,除非设置了timeout并发生超时

import time
import threading
from sys import stdout

event = threading.Event()


class GoHome(threading.Thread):
    def __init__(self, name):
        super().__init__()
        self.name = name

    def run(self):
        string1 = self.name + "收拾好书包了\n"
        # time.sleep(1)
        stdout.write(string1)
        event.wait()
        string2 = self.name + "飞快跑出教室\n"
        time.sleep(1)
        stdout.write(string2)


t1 = GoHome("张三")
t2 = GoHome("王五")
t3 = GoHome("李四")
t1.start()
t2.start()
t3.start()
stdout.write("ding~ding~ding~ding~~~~~~~~~~~~~~\n")
event.set()

执行结果

张三收拾好书包了
王五收拾好书包了
李四收拾好书包了
ding~ding~ding~ding~~~~~~~~~~~~~~
王五飞快跑出教室
李四飞快跑出教室
张三飞快跑出教室

进程已结束,退出代码为 0

练习

使用类继承的方式,实现信号量、事件功能操作。具体案例:第一个线程中获取当前时间,判断当前时间3秒之后,触发“事件” 对象。在另一个线程中,作为数学考试结束的判断变量,否则一直处于考试中,并打印。

# !/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2022/1/19 18:09
# @Author  : 波澜不惊
# @File    : 题1.py
# 使用类继承的方式,实现信号量、事件功能操作。具体案例:第一个
# 线程中获取当前时间,判断当前时间3秒之后,触发“事件” 对象。在另
# 一个线程中,作为数学考试结束的判断变量,否则一直处于考试中,并打
# 印。

import time
import threading

event = threading.Event()
semaphore = threading.Semaphore(2)


class TimeCloser(threading.Thread):
    def run(self):
        if semaphore.acquire():
            print("现在时间是"+time.ctime())
            time.sleep(3)
            event.set()
            semaphore.release()


class Test(threading.Thread):
    def run(self):
        if semaphore.acquire():
            while True:
                if event.is_set():
                    print("考试结束!")
                    break
                else:
                    print("考试中")
                    time.sleep(0.5)
            semaphore.release()


timeCloser = TimeCloser()
test = Test()
timeCloser.start()
test.start()

执行结果

现在时间是Thu Jan 20 23:37:06 2022
考试中
考试中
考试中
考试中
考试中
考试中
考试结束!

进程已结束,退出代码为 0
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值