python同步原语--线程锁
多线程锁是python多种同步原语中的其中一种。首先解析一下什么是同步原语,python因为GIL(全局解析锁)的缘故,并没有真正的多线性。另外python的多线程存在一个问题,在多线程编程时,会出现线程同时调用共同的存储空间而导致错误的出现(即‘竞态行为’)。虽然许多专家建议python开发者在处理并发的时候弃用多线程而用多进程,但是在I/O操作这种短时间的操作上(通常GIL锁在这段时间内已经释放),多线程编程还是有很大的优势的。而在计算密集型的编程时,本人还是觉得用多进程比较稳妥。
在处理多线程的‘竞态行为’的问题上,python提供了不少解决的方法--同步原语,例如:锁,事件,信号量等。
所以问题回归到锁添加的原因和加锁的优势:
在多线程同时进入临界资源区获取和操作共有资源时,会出现资源的争夺而出现混乱。为了避免这种混乱现象,python提出了锁机制,能够实现多线程程序的同步执行,从而避免因争夺资源而出现错误。
线程锁的定义和运用
一、创建锁对象:
语法:
lock = Lock()
锁对象一旦创建,就可以随时被进程或者线程调用,并且一次创建锁只有一把,如果多个资源想同时获取锁,必须‘排队’,等上一个进程/线程释放了锁才可以请求获取锁
二、上锁(也叫请求锁)
语法:
lock.acquire()
acquire()是一个阻塞函数。一旦请求获取锁成功,就会把下面将要执行的程序的变量内存空间‘锁住’;而获取不成功则会一直阻塞在那里,等待上一个获得锁的进程/线程
释放锁。
三、解锁
lock.release()
锁的创建、获取和释放其实很简单,而在实际运用中还需要处理一些比较复杂的问题。下面谈谈死锁的问题。
死锁和可重入锁
死锁的出现有两种情况:
1) 当一个进程或者一个线程一直调用或者占用同一锁Lock而不释放资源而导致其他进程/线程无法获得锁,就会出现的死锁状况,一直阻塞在aquire()处
2) 当有两个线程同时想获取两个锁的时候(再往上推就是多个线程想获取多个锁甚至是一个线程想获取多个锁),例如递归函数(一个线程获取多个锁)的使用。由于两者都是处于竞争关系,谁也不让谁,谁快谁得手,但计算机中这种竞争关系是很微妙的,时间的差异性很小,于是,就出现了两者都阻塞在同一个地方,都无法同时获得两个锁或者获取对方已经获取的但还没有释放的锁。
为了解决死锁的问题,于是python提出了可重入锁的机制(RLock)
重入锁定义后,一个进程就可以重复调用指定次数的一个重入锁,而不用去跟别的进程一起争夺其他锁。
重入锁中内部管理者两个对象,即Lock对象和锁的调用次数count
下面说说RLock到底是怎么用的
1)RLock的定义
mutexA = mutexB = RLock( )
mutex值可以是多个的,定义了多少个,RLock内部的count就为几
2)RLock的请求
mutexA.acquire()
mutexA.acquire()
上面这两行代码看似是一样的,但其实是两次请求锁。每申请一次锁,Rlock内部的count就会减小1,两次请求过后,count从2减为0
因为上面定义的重入锁的内部个数为2,所以该重入锁可以被一个进程调用两次,并且在虽然它内部有多个锁,但只能由一个进程/线程调用,其他进程/线程不能干预,只有当这个进程/线程释放掉所有的重入锁,count重新变为count=2时才可以被其他进程/线程调用。
3)RLock锁的释放
mutexA.release()
mutexB.release()
完整代码举例:
from multiprocessing import RLock,Process
from time import ctime,sleep mutexA = mutexB =RLock() #定义可重入锁,内部有两个锁,即mutexA和mutexB def fn1():
mutexA.acquire()
sleep(1)
print(ctime(),'进程1获取A锁')
mutexB.acquire()
sleep(2)
print(ctime(),'进程1获取B锁')
mutexA.release()
print('进程1释放A锁')
mutexB.release()
print('进程1释放B锁') def fn2():
mutexA.acquire()
sleep(1)
print(ctime(),'进程2获取A锁')
mutexB.acquire()
sleep(1)
print(ctime(),'进程2获取B锁')
mutexA.release()
print('进程2释放A锁')
mutexB.release()
print('进程2释放B锁') p1 = Process(target=fn1) #定义进程1
p2 = Process(target=fn2) #定义进程2
p1.start() #开启进程1
p2.start() #开启进程2 p1.join() #回收进程空间
p2.join()
注意:在实际开发中我们当然不会用sleep()这种方法来实现等待程序的执行(这是很愚蠢的一种行为),在这里只是作为一种演示,为了把逻辑比较清晰地表达出来。实际开发中通常是在成功执行一段程序,在获取预定的结果之后,再手动地释放锁。
那么上面的程序运行结果如下:

那么如果我让进程2先开启呢?

结果如下:

第一个案例中因为p1的进程比p2的进程更早运行(程序在运行顺序上更早),所以p1先获取了可重入锁。而在案例2中,p2就比p1更早地获取可重入锁了。
显然,从这两个案例中我们发现,锁的获得是谁快谁得手。同时也验证了我上面描述的,一个进程对一个可重入锁的请求是排他型的,一旦这个进程请求了
一个可重入锁,那么其他进程就无法再请求了,直到这个进程释放了可重入锁内部的所有锁。
python同步原语--线程锁的更多相关文章
- python中的线程锁
锁对象 原始锁是一个在锁定时不属于特定线程的同步基元组件.在Python中,它是能用的最低级的同步基元组件,由 _thread 扩展模块直接实现. 原始锁处于 "锁定" 或者 &q ...
- python网络编程--线程锁(互斥锁Mutex)
一:为什么需要线程锁 一个进程下可以启动多个线程,多个线程共享父进程的内存空间,也就意味着每个线程可以访问同一份数据,此时,如果2个线程同时要修改同一份数据,会出现什么状况? 很简单,假设你有A,B两 ...
- Python GIL、线程锁、信号量及事件
GIL是什么? GIL并不是Python的特性,它是在实现Python解析器(CPython)时所引入的一个概念.就好比C++是一套语言(语法)标准,但是可以用不同的编译器来编译成可执行代码.有名的编 ...
- python 多线程、线程锁、事件
1. 多线程的基本使用 import threading import time def run(num): print('Num: %s'% num) time.sleep(3) if num == ...
- python网络编程--线程(锁,GIL锁,守护线程)
1.线程 1.进程与线程 进程有很多优点,它提供了多道编程,让我们感觉我们每个人都拥有自己的CPU和其他资源,可以提高计算机的利用率.很多人就不理解了,既然进程这么优秀,为什么还要线程呢?其实,仔细观 ...
- python同步、互斥锁、死锁
目录 同步 同步的概念 解决线程同时修改全局变量的方式 互斥锁 使用互斥锁完成2个线程对同一个全局变量各加9999999 次的操作 上锁解锁过程 总结 死锁 避免死锁 同步 同步的概念 同步就是协同步 ...
- python多线程、线程锁
1.python多线程 多线程可以把空闲时间利用起来 比如有两个进程函数 func1.func2,func1函数里使用sleep休眠一定时间,如果使用单线程调用这两个函数,那么会顺序执行这两个函数 也 ...
- Python并发编程-线程锁
互斥锁-Lock #多线程中虽然有GIL,但是还是有可能产生数据不安全,故还需加锁 from threading import Lock, Thread #互斥锁 import time def ea ...
- python线程同步原语--源码阅读
前面两篇文章,写了python线程同步原语的基本应用.下面这篇文章主要是通过阅读源码来了解这几个类的内部原理和是怎么协同一起工作来实现python多线程的. 相关文章链接:python同步原语--线程 ...
随机推荐
- Ubuntu 18.0.4安装Mongodb
2.21更新: 安装后本地通过robo 3T连接正常,但是其它机器通过IP连接时报错,继续查找,解决方案在这里(传送门),原因是mongodb安装完成后默认监听本地地址,也就是127.0.0.1,这样 ...
- NHibernate删除数据时遇到deleted object would be re-saved by cascade级联问题
今天在处理数据时遇到了这个问题,数据是一对多的关系,A包含多个B,想将某个B从A中移除,在保存时抛出如标题的问题,查找了下资料得知:由于在配置文件中设置了cascade为all,A和B存在级联关系,那 ...
- [源码]K8 Cscan模块 C#获取内网主机IP/机器名/Banner/网页标题源码
[原创]K8 Cscan 大型内网渗透自定义扫描器 https://www.cnblogs.com/k8gege/p/10519321.html Cscan简介:何为自定义扫描器?其实也是插件化,但C ...
- [视频]K8飞刀 BadUSB Teensy自动种马演示教程
[视频]K8飞刀 Teensy USB自动种马演示教程 链接: https://pan.baidu.com/s/13bM1XSLrhlf90FDmPGfo1g 提取码: gy2q 源码:https:/ ...
- C# Winform同时启动多个窗体类
首先创建一个类,存放将要同时显示的窗体 using System; using System.Collections.Generic; using System.Linq; using System. ...
- 课程四(Convolutional Neural Networks),第四 周(Special applications: Face recognition & Neural style transfer) —— 1.Practice quentions
[解释] This allows us to learn to predict a person’s identity using a softmax output unit, where the n ...
- JsExcelXml.js的源码
var JSXmlExcel = { ConvertXmlDoc: function (text) { var xmlDoc = null; try { xmlDoc = new ActiveXObj ...
- 从零开始学 Web 之 DOM(一)DOM的概念,对标签操作
大家好,这里是「 Daotin的梦呓 」从零开始学 Web 系列教程.此文首发于「 Daotin的梦呓 」公众号,欢迎大家订阅关注.在这里我会从 Web 前端零基础开始,一步步学习 Web 相关的知识 ...
- 逆向知识之CS辅助/外挂专题.2.实现CS1.6无限夜视仪.无限闪光烟雾高爆弹.
逆向知识之CS辅助/外挂专题.2.实现CS1.6无限夜视仪.无限闪光烟雾高爆弹. 关于人物子弹无限可以观看上一篇博客. 一丶无限夜视仪. 无限夜视仪找法. 1.CE附加游戏. 2.搜索0或者1. 3. ...
- thinkphp设置默认访问的模块
在index.php中加入(可以省略Home,直接写控制器和方法访问) define('BIND_MODULE','Home'); 在config.php里边加入 'MODULE_ALLOW_LIST ...