Python | threading04 - 使用信号量,实现线程间同步

一、前言


先说一下信号量与互斥量之间的关系。信号量的一个特殊的应用是互斥锁(互斥量),当信号量的最大数量是1时,它跟互斥锁(互斥量)的作用相同。

以下摘自《python并行编程》中文版:
在这里插入图片描述

二、生产者-消费者模型


本次的代码目的如下:

  1. 生成者producer每隔3秒时间同步一次消费者1(customer1)与消费者2(customer2)。
  2. 消费者1(customer1)与消费者2(customer2)每一次被同步都可以运行两次。
    在这里插入图片描述

2.1、代码

# python3.9
import time
import threading

se1 = threading.Semaphore(0)  # se1信号量的初始数量是0
se2 = threading.Semaphore(0)  # se2信号量的初始数量是0

# 消费者1线程函数
def customer1():
    global se1
    while True:
        # print("customer1请求se1信号量。")
        se1.acquire()  # 向se1信号量请求一个信号
        print("customer1请求信号量成功,time:%s" % time.perf_counter())

# 消费者2线程函数        
def customer2():
    global se2
    while True:
        # print("customer2请求se2信号量。")
        se2.acquire()  # 向se1信号量请求一个信号
        print("customer2请求信号量成功,time:%s" % time.perf_counter())

# 生产者线程函数
def producer():
    while True:
        time.sleep(3)  # 休眠3秒钟
    
        # 释放se1两个信号
        se1.release()
        se1.release()
    
        # 释放se2两个信号
        se2.release()
        se2.release()
        print("producer释放完信号量,其他线程将被同步。time:%s" % time.perf_counter())

# 主线程函数
def main():
    t1 = threading.Thread(target=producer,name="thread_producer",daemon=True)    # 创建producer子线程
    t2 = threading.Thread(target=customer1,name="thread_customer1",daemon=True)  # 创建cusotmer1子线程
    t3 = threading.Thread(target=customer2,name="thread_customer2",daemon=True)  # 创建customer2子线程
    t1.start()  # 启动producer线程 
    t2.start()  # 启动customer1线程
    t3.start()  # 启动customer2线程
    t1.join()   # 子线程producer是无限循环的线程,所以主线程需要等待它运行结束
    t2.join()   # 子线程customer1是无限循环的线程,所以主线程需要等待它运行结束
    t3.join()   # 子线程customer2是无限循环的线程,所以主线程需要等待它运行结束
    print("主线程运行结束!")
    
    
    
if __name__ == "__main__":
    main()

2.2、运行的结果

在这里插入图片描述
python多线程的信号量跟单片机RTOS的信号量真的很相似。如果之前接触过RTOS的信号量的话,python的信号量也很快可以掌握。

2.3、Semaphore没有设置上限值

在代码中,只设置了信号量的初始值。
在这里插入图片描述
因为我将信号量的初始值设为0,所以线程customer1与线程customer2在刚被创建的时候就进入了阻塞态(因为没有信号量)。
在这里插入图片描述
Semaphore没有设置上限值,所以可能会出现信号量释放的次数过多(多于本来规划的值)。为了避免这个情况的出现,Python的多线程还有另外一个工具叫做有界信号量。
单片机的RTOS上的信号量跟python的有界信号量一样,能防止信号量释放的次数过多。

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
在多线程编程中,线同步是非常重要的。线同步是指在多个线程并发访问共享资源时,为了避免出现数据不一致或其他问题,需要对线程的执行进行协调和控制。常见的线同步方式包括锁、信号、条件变等。 在 Python 中,线同步可以通过 threading 模块中的 Lock 类来实现。Lock 类提供了 acquire() 和 release() 方法,用于控制资源的访问。 使用 Lock 类的基本流程如下: 1. 创建 Lock 对象。 2. 在需要访问共享资源的代码块前调用 acquire() 方法获取锁,阻塞其他线程对该资源的访问。 3. 在访问共享资源的代码块后调用 release() 方法释放锁,允许其他线程对该资源的访问。 下面是一个使用 Lock 类实现线同步的示例: ```python import threading # 共享资源 count = 0 # 创建 Lock 对象 lock = threading.Lock() def add(): global count for i in range(100000): # 获取锁 lock.acquire() count += 1 # 释放锁 lock.release() def sub(): global count for i in range(100000): # 获取锁 lock.acquire() count -= 1 # 释放锁 lock.release() # 创建两个线程 t1 = threading.Thread(target=add) t2 = threading.Thread(target=sub) # 启动线程 t1.start() t2.start() # 等待线程执行结束 t1.join() t2.join() print(count) ``` 在上面的示例中,我们创建了一个共享资源 count,然后分别创建了两个线程对该资源进行加和减操作。在访问共享资源的代码块前,我们使用 acquire() 方法获取锁,阻塞其他线程对该资源的访问;在访问共享资源的代码块后,我们使用 release() 方法释放锁,允许其他线程对该资源的访问。 需要注意的是,获取锁和释放锁的代码必须成对出现,否则会出现死锁等问题。此外,在使用锁进行线同步时,应尽避免持锁时间过长,以免影响程序的性能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Wallace Zhang

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

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

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

打赏作者

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

抵扣说明:

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

余额充值