信号量(Semaphore)是一个同步工具

简介: 信号量(Semaphore)是一个同步工具

一、信号量(Semaphore)概述

信号量(Semaphore)是一个同步工具,用于控制对共享资源的访问。在Python的threading模块中,Semaphore类提供了一种方式来限制对某个代码块的并发访问数量。信号量内部维护了一个计数器,表示当前可用的资源数量。当计数器大于0时,acquire()方法会将其减1并立即返回,允许线程继续执行。当计数器为0时,acquire()方法会阻塞,直到其他线程调用release()方法增加计数器。

二、Python代码示例

import threading
import time
import random

# 定义一个共享资源,例如一个银行账户
balance = 0

# 定义一个信号量,表示同时访问共享资源的线程数量
semaphore = threading.Semaphore(2)

def withdraw(amount):
    global balance
    # 获取信号量,如果信号量计数器为0,则阻塞
    semaphore.acquire()
    try:
        # 模拟取款操作,这里只是简单地减少余额
        if balance >= amount:
            balance -= amount
            print(f"线程 {threading.current_thread().name} 成功取款 {amount},当前余额为 {balance}")
        else:
            print(f"线程 {threading.current_thread().name} 取款失败,余额不足")
    finally:
        # 释放信号量,无论取款是否成功
        semaphore.release()

def main():
    # 创建多个线程模拟并发取款操作
    for i in range(10):
        amount = random.randint(10, 100)  # 每次取款的金额是随机的
        t = threading.Thread(target=withdraw, args=(amount,), name=f"取款线程{i+1}")
        t.start()

    # 等待所有线程完成
    for t in threading.enumerate():
        if t != threading.current_thread():
            t.join()

    print("所有取款操作完成,最终余额为:", balance)

if __name__ == "__main__":
    main()

三、代码解释

1. 导入必要的模块

  • threading:Python的线程模块,提供了创建和管理线程的功能。
  • time:虽然在这个示例中没有直接使用,但通常用于线程间的同步和延时。
  • random:用于生成随机的取款金额。

2. 定义共享资源和信号量

  • balance:表示银行账户的余额,是一个全局变量,多个线程都会访问和修改它。
  • semaphore:一个threading.Semaphore对象,用于控制同时访问balance的线程数量。这里我们设置计数器为2,表示同时最多有两个线程可以访问balance

3. 定义取款函数

  • withdraw(amount):模拟取款操作。首先,通过调用semaphore.acquire()获取信号量。如果信号量的计数器大于0,则立即返回并继续执行取款逻辑;如果计数器为0,则阻塞等待。取款逻辑完成后(无论是否成功),都通过调用semaphore.release()释放信号量。

4. 主函数

  • main():创建多个线程并启动它们,模拟并发取款操作。每个线程都会调用withdraw()函数进行取款。通过调用t.join()等待所有线程完成。

5. 运行程序

  • 当程序作为主模块运行时(即直接运行这个Python文件而不是作为模块导入),会调用main()函数启动程序。

四、深入讨论

1. 信号量的作用

  • 限制并发访问:通过信号量,我们可以限制同时访问某个共享资源的线程数量,从而避免资源竞争和冲突。
  • 实现同步:信号量提供了一种同步机制,确保在特定时刻只有一个或少数几个线程可以访问共享资源。
  • 防止死锁:与锁(Lock)相比,信号量更灵活,更容易避免死锁。因为信号量允许同时有多个线程访问共享资源,而锁则只允许一个线程访问。

2. 信号量的使用场景

  • 数据库连接池:在Web应用中,为了提高性能和减少资源消耗,通常会使用数据库连接池来管理数据库连接。通过信号量,我们可以限制同时从连接池中获取连接的线程数量。
  • 文件读写:当多个线程需要同时读写同一个文件时,可以使用信号量来确保在任意时刻只有一个线程在写文件,而读文件的线程数量可以稍多一些。
  • 网络请求:在
    处理结果:

    一、信号量(Semaphore)概述

    信号量(Semaphore)是一个同步工具,用于控制对共享资源的访问。在Python的threading模块中,Semaphore类提供了一种方式来限制对某个代码块的并发访问数量。信号量内部维护了一个计数器,表示当前可用的资源数量。当计数器大于0时,acquire()方法会将其减1并立即返回,允许线程继续执行。当计数器为0时,acquire()方法会阻塞,直到其他线程调用release()方法增加计数器。

    二、Python代码示例

    ```python

    定义一个共享资源,例如一个银行账户

    定义一个信号量,表示同时访问共享资源的线程数量

    def withdraw(amount)_
    global balance

    获取信号量,如果信号量计数器为0,则阻塞

    semaphore.acquire()
    try_

    模拟取款操作,这里只是简单地减少余额

    if balance >= amount_
    balance -= amount
    print(f"线程 {threading.currentthread().name} 成功取款 {amount},当前余额为 {balance}")
    else

    print(f"线程 {threading.currentthread().name} 取款失败,余额不足")
    finally

    释放信号量,无论取款是否成功

    semaphore.release()
    def main()_

    创建多个线程模拟并发取款操作

    for i in range(10)_
    amount = random.randint(10, 100) # 每次取款的金额是随机的
    t = threading.Thread(target=withdraw, args=(amount,), name=f"取款线程{i+1}")
    t.start()

    等待所有线程完成

    for t in threading.enumerate()_
    if t != threading.currentthread()
    t.join()
    print("所有取款操作完成,最终余额为:", balance)
    if name == "main"_
    main()

    1. 导入必要的模块

  • threading:Python的线程模块,提供了创建和管理线程的功能。

    2. 定义共享资源和信号量

  • balance:表示银行账户的余额,是一个全局变量,多个线程都会访问和修改它。

    3. 定义取款函数

  • withdraw(amount):模拟取款操作。首先,通过调用semaphore.acquire()获取信号量。如果信号量的计数器大于0,则立即返回并继续执行取款逻辑;如果计数器为0,则阻塞等待。取款逻辑完成后(无论是否成功),都通过调用semaphore.release()释放信号量。

    4. 主函数

  • main():创建多个线程并启动它们,模拟并发取款操作。每个线程都会调用withdraw()函数进行取款。通过调用t.join()等待所有线程完成。

    5. 运行程序

  • 当程序作为主模块运行时(即直接运行这个Python文件而不是作为模块导入),会调用main()函数启动程序。

    四、深入讨论

    1. 信号量的作用

  • 限制并发访问:通过信号量,我们可以限制同时访问某个共享资源的线程数量,从而避免资源竞争和冲突。

    2. 信号量的使用场景

  • 数据库连接池:在Web应用中,为了提高性能和减少资源消耗,通常会使用数据库连接池来管理数据库连接。通过信号量,我们可以限制同时从连接池中获取连接的线程数量。
相关文章
|
JavaScript 前端开发 Java
|
Ubuntu 安全 Linux
《Ubuntu 24.04.1版安装全攻略与实测体验》
综上所述,这次关于Ubuntu 24.04.1版的安装经历让我对新版本充满了期待,尽管细节上有些微的变化,但整体体验显得更加便捷易懂。在这一波Ubuntu新气象中,我期待与各位一起分享更多新鲜的体验与感受。
|
数据采集 存储 Web App开发
轻量级爬虫框架Feapder入门:快速搭建企业级数据管道
本教程基于Feapder框架,讲解如何构建轻量级爬虫采集豆瓣电影数据。通过配置代理IP、Cookie与User-Agent,实现企业级数据管道能力,包括动态请求与信息提取(如电影名称、导演、演员等)。适合具备Python基础及爬虫原理知识的读者,提供从环境搭建到代码实现的完整流程,并分析常见错误与解决方法,助力高效开发。
896 1
轻量级爬虫框架Feapder入门:快速搭建企业级数据管道
|
测试技术 UED
软件测试中的“灰盒”方法:一种平衡透明度与效率的策略
在软件开发的复杂世界中,确保产品质量和用户体验至关重要。本文将探讨一种被称为“灰盒测试”的方法,它结合了白盒和黑盒测试的优点,旨在提高测试效率同时保持一定程度的透明度。我们将通过具体案例分析,展示灰盒测试如何在实际工作中发挥作用,并讨论其对现代软件开发流程的影响。
|
安全 Linux 网络安全
车载操作系统信息安全架构
本次分享的主题是车载操作系统信息安全架构,由中兴通讯操作系统产品部张兵分享。主要分为以下四个部分: 1. 背景 2. 现状 3. 实践 4. 展望
687 1
|
分布式计算 并行计算 数据处理
Pandas高级数据处理:并行计算
Pandas是Python中广泛使用的数据分析库,随着数据量增加,单线程处理速度成为瓶颈。本文介绍Pandas并行计算的基本概念、方法及常见问题的解决方案。并行计算通过多线程、多进程或分布式框架(如Dask)实现,充分利用多核CPU优势。文章详细解释了数据分割、内存占用和线程/进程间通信等问题,并提供了代码示例。最后总结了常见报错及其解决方法,帮助开发者提升数据处理效率。
724 3
|
JavaScript 前端开发 测试技术
在 golang 中执行 javascript 代码的方案详解
本文介绍了在 Golang 中执行 JavaScript 代码的四种方法:使用 `otto` 和 `goja` 嵌入式 JavaScript 引擎、通过 `os/exec` 调用 Node.js 外部进程以及使用 WebView 嵌入浏览器。每种方法都有其适用场景,如嵌入简单脚本、运行复杂 Node.js 脚本或在桌面应用中显示 Web 内容。
1502 15
在 golang 中执行 javascript 代码的方案详解
|
网络协议 安全 网络架构
NAT穿透技术、穿透原理和方法
【10月更文挑战第19天】 NAT穿透技术、穿透原理和方法
1997 7
|
API 数据安全/隐私保护 开发者
实时获取小红书详情 API 数据
小红书详情API数据获取指南:注册开发者账号,创建应用并申请接口权限,构建请求获取笔记详情,使用Python等语言处理响应数据。需遵守使用规则,注意调用频率和数据安全。