登 录
注 册
< 编程语言
Python
Java
Go
SQL
数据结构与算法
极速安装
线程同步与信号量
生产者消费者
Python调用Java
Python自动发送邮件
数据导出工具
热门推荐>>>
中台架构
中台建设与架构
Hadoop
源码分析-NN启动(三)
HBase
HBased对接Hive
Linux
Nginx高可用
Flink
3分钟搭建Flink SQL测试环境
Kafka
Kafka对接Flume
深度学习
卷积神经网络
数据结构与算法
选择合适的算法
MySQL
数据备份恢复
计算机系统
信号量同步线程
Hive
Hive调优参数大全
其他框架
Azkaban Flow1.0与2.0
ClickHouse
表引擎-其他类型
技术成长
最好的职业建议
精选书单
技术成长书单—机器学习
技术资讯
数据在线:计算将成为公共服务
开发工具
IntelliJ IDEA 20年发展回顾(二)
系统工具
Mac命令行工具
虚拟化
内存虚拟化概述
云原生
云原生构建现代化应用
云服务
一文搞懂公有云、私有云...
Java
Spring Boot依赖注入与Runners
Go
Go函数与方法
SQL
SQL模板
安全常识
一文读懂SSO
当前位置:
首页
>>
Python
>>
线程同步与信号量
线程同步与信号量
2020-07-02 23:43:18 星期四 阅读:1756
 一般在多线程代码中,总会有一些特定的函数或者代码块不希望(或不应该)被多个线程同时执行,通常包括修改数据库、更新文件或者其他会产生竞态条件的类似情况。当任意数量的线程可以访问临界区的代码,但在给定的时刻只有一个线程可以通过时,就是使用`线程同步`的时候了。Python支持多种同步类型,可以给你足够多的选择,以便选出适合完成任务的那种类型。 这里介绍两种类型的同步原语: ``` 锁/互斥:所有类型中最简单、最低级的机制。 信号量:经常用语多线程竞争有限资源的情况。 ``` ####锁 锁有两种状态,锁定和未锁定,而且它只支持两个函数,获得锁和释放锁。当多线程争夺锁时,允许第一个获得锁的线程进入临界区,并执行代码。所有之后到达 的线程将被阻塞,直到第一个线程执行结束,退出临界区,并释放锁。此时,其他等待的线 程可以获得锁并进入临界区。不过请记住,那些被阻塞的线程是没有顺序的(即不是先到先 执行),胜出线程的选择是不确定的,而且还会根据 Python 实现的不同而有所区别。 ####信号量示例 如前所述,锁非常易于理解和实现,也很容易决定何时需要它们。然而,如果情况更加复杂,你可能需要一个更强大的同步原语来代替锁。对于拥有有限资源的应用来说,使用信号量可能是个不错的决定。 信号量是最古老的同步原语之一。它是一个计数器,`当资源消耗时递减,当资源释放时递增`。你可以认为信号量代表它们的资源可用或不可用。消耗资源使计数器递减的操作习惯上称为P,也称为wait、try、acquire、pend或procure。 相对地,当一个线程对一个资源完成操作时,该资源需要返回资源池中。这个操作一般称为 V,也称为 signal、increment、release、post、vacate。 Python 简化了所有的命名,使用和锁的函数/方法一样的名字:acquire 和 release。信号量比锁更加灵活,因为可以有多个线程,每个线程拥有有限资源的一个实例。 在下面的例子中,模拟一个简化的糖果机。这个特制的机器只有 5 个可用的槽来 保持库存(糖果)。如果所有的槽都满了,糖果就不能再加到这个机器中了;相似地,如果每个槽都空了,想要购买的消费者就无法买到糖果了。我们可以使用信号量来跟踪这些有限的 资源(糖果槽)。 ```python import random import time import threading # 定义一个锁 lock = threading.Lock() # 表示库存商品的最大值 MAX = 5 # 创建一个信号量对象,负责检查当前的值有没有超过它的初始值,一旦超过了就会抛出异常 # 信号量其实就是计数器,它们从固定数量的有限资源起始 # 当分配一个单位的资源时,计数器减1,而当一个单位的资源返回资源池时,计数器加1 # BoundedSemaphore的一个额外功能就是这个计数器的值永远不会超过它的初始值 bs = threading.BoundedSemaphore(MAX) # 添加糖果 def refill(): lock.acquire() print("正在填充糖果...") try: # 判断糖果是不是满的 # 也就是分配一个单位的资源,计数器-1 bs.release() except ValueError: print("糖果是满的,跳过") else: print("糖果填充完毕") lock.release() def buy(): lock.acquire() print("正在购买糖果") # 判断资源池里是否还有资源,如果有,则购买成功,计数器+1 if bs.acquire(False): print("购买糖果成功") else: print("糖果库是空的,不能购买") lock.release() def producer(loops): for i in range(loops): refill() time.sleep(random.randint(1, 5) / 2) def consumer(loops): for i in range(loops): buy() time.sleep(random.randint(1, 5) / 2) def main(): print("starting at: {}".format(time.ctime())) nloops = random.randint(2, 6) # 创建消费者线程进行了额外的数学操作,用于随机给出正偏差,使得消费者真正消费的糖果数可能会比生产者放入机器的更多 # 否则,代码将永远不会进入消费者尝试从空机器购买糖果的情况 t1 = threading.Thread(target=consumer, args=(random.randint(nloops, nloops + MAX + 2),)) t2 = threading.Thread(target=producer, args=(nloops,)) t1.start() t2.start() if __name__ == "__main__": main() print("all DONE as:{}".format(time.ctime())) ``` 控制台输出如下 ``` /usr/local/bin/python3.6 /Users/libins/VsCode/test/my_multiprocessing.py starting at: Thu Sep 19 14:08:37 2019 正在购买糖果 购买糖果成功 正在填充糖果... 糖果填充完毕 all DONE as:Thu Sep 19 14:08:37 2019 正在填充糖果... 糖果是满的,跳过 正在购买糖果 购买糖果成功 正在购买糖果 购买糖果成功 正在购买糖果 购买糖果成功 Process finished with exit code 0 ```