走入计算机的第三十六天(自己创建锁及锁的类型)

python thread模块 锁 同步锁

Python中的线程是操作系统的原生线程,Python虚拟机使用一个全局解释器锁(Global Interpreter Lock)来互斥线程对Python虚拟机的使用。为了支持多线程机制,一个基本的要求就是需要实现不同线程对共享资源访问的互斥,所以引入了GIL。
GIL:在一个线程拥有了解释器的访问权之后,其他的所有线程都必须等待它释放解释器的访问权,即使这些线程的下一条指令并不会互相影响。
在调用任何Python C API之前,要先获得GIL
GIL缺点:多处理器退化为单处理器;优点:避免大量的加锁解锁操作.

2.3.1 GIL的早期设计

Python支持多线程,而解决多线程之间数据完整性和状态同步的最简单方法自然就是加锁。 于是有了GIL这把超级大锁,而当越来越多的代码库开发者接受了这种设定后,他们开始大量依赖这种特性(即默认python内部对象是thread-safe的,无需在实现时考虑额外的内存锁和同步操作)。慢慢的这种实现方式被发现是蛋疼且低效的。但当大家试图去拆分和去除GIL的时候,发现大量库代码开发者已经重度依赖GIL而非常难以去除了。有多难?做个类比,像MySQL这样的“小项目”为了把Buffer Pool Mutex这把大锁拆分成各个小锁也花了从5.5到5.6再到5.7多个大版为期近5年的时间,并且仍在继续。MySQL这个背后有公司支持且有固定开发团队的产品走的如此艰难,那又更何况Python这样核心开发和代码贡献者高度社区化的团队呢?

2.3.2 GIL的影响

无论你启多少个线程,你有多少个cpu, Python在执行一个进程的时候会淡定的在同一时刻只允许一个线程运行。
所以,python是无法利用多核CPU实现多线程的。
这样,python对于计算密集型的任务开多线程的效率甚至不如串行(没有大量切换),但是,对于IO密集型的任务效率还是有显著提升的。

所以对于GIL,既然不能反抗,那就学会去享受它吧!

同步锁

锁通常被用来实现对共享资源的同步访问。为每一个共享资源创建一个Lock对象,当你需要访问该资源时,调用acquire方法来获取锁对象(如果其它线程已经获得了该锁,则当前线程需等待其被释放),待资源访问完后,再调用release方法释放锁。

 1 import threading
 2
 3 import time
 4 def sub():
 5     global num   #引用全局变量
 6     lock.acquire()     #启动锁
 7     temp=num      #赋值变量
 8     time.sleep(0.1)   睡眠
 9     num=temp-1        #变量自减一
10     lock.release()      #关闭锁
11
12     time.sleep(2)   #睡眠时间
13 num=100   #全局变量
14 l=[]     #定义空的字典
15 lock=threading.Lock()     #创建锁
16 for i in range(100):  # #创建线程的数量
17     t=threading.Thread(target=sub,args=())    #创建对象线程
18     t.start()     #启动对象线程
19     l.append(t)   #将对象线程写入空字典中
20 for t in l:
21     t.join()    #主线程运行完后,子线程才能运行
22 print(num)

import threading

R=threading.Lock()

R.acquire()
‘‘‘
对公共数据的操作
‘‘‘
R.release()

所谓死锁: 是指两个或两个以上的进程或线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。

 1 import threading
 2 import time
 3 class MyThread(threading.Thread):   ##创建一个子类,父类是(threading.Thread)
 4     def __init__(self):
 5         threading.Thread.__init__(self)         #调用父类的功能
 6     def run(self):              #用父类的功能
 7         self.foo()             #调用自己的功能
 8         self.fo()                #调用自己的功能
 9     def foo(self):           #创建自己的功能
10         LockA.acquire()        #开启大锁
11         print(‘qqqqq‘)         #启动功能
12         LockB.acquire()       #开启小锁
13         print(‘wwwww‘)      #启动功能
14         LockB.release()       #关闭小锁
15         LockA.release()      #关闭大锁
16     def fo(self):          #创建自己的功能
17         LockB.acquire()   #开启小锁
18         print(‘lllll‘)      #启动功能
19         LockA.acquire()       #开启大锁
20         time.sleep(1)
21         print(‘dddddd‘)      #启动功能
22         LockA.release()       #关闭大锁
23         LockB.release()      #关闭小锁
24 LockA=threading.Lock()       #创建大锁
25 LockB=threading.Lock()       #创建小锁
26 for i in range(10):       #创建线程的数量
27     t=MyThread()      #创建对象
28     t.start()         #启动对象  #两个锁同时运行,但是两个锁会打结,一个等待着一个
29     t.run()       #对象调用父类下的功能,相当于串型

在Python中为了支持在同一线程中多次请求同一资源,python提供了可重入锁RLock。这个RLock内部维护着一个Lock和一个counter变量,counter记录了acquire的次数,从而使得资源可以被多次require。直到一个线程所有的acquire都被release,其他的线程才能获得资源。上面的例子如果使用RLock代替Lock,则不会发生死锁。

 1 import threading
 2 import time
 3 class MyThread(threading.Thread):    #调用父类的功能
 4     def __init__(self):
 5         threading.Thread.__init__(self)
 6     def run(self):
 7         self.foo()
 8         self.fo()
 9     def foo(self):
10         Lock.acquire()     #
11         print(‘qqqqq‘)
12         Lock.acquire()
13         print(‘wwwww‘)
14         Lock.release()
15         Lock.release()
16     def fo(self):
17         Lock.acquire()
18         print(‘qqqqq‘)
19         time.sleep(1)
20         Lock.acquire()
21         print(‘wwwww‘)
22         Lock.release()
23         Lock.release()
24 Lock=threading.RLock()   #创建一个锁R
25 for i in range(10):
26     t=MyThread()
27     t.start()

Semaphore管理一个内置的计数器,
每当调用acquire()时内置计数器-1;
调用release() 时内置计数器+1;
计数器不能小于0;当计数器为0时,acquire()将阻塞线程直到其他线程调用release()。

实例:(同时只有5个线程可以获得semaphore,即可以限制最大连接数为5):

 1 import threading
 2 import time
 3
 4 semaphore=threading.Semaphore(5)   #创建同时执行的数量
 5 def foo():      #创建对象
 6     semaphore.acquire()    #启动执行的数量
 7     time.sleep(0.1)     #睡眠时间
 8     print(‘sb‘)     #打印
 9     semaphore.release()      #关闭执行数量
10
11 for i in range(100):  #创建线程的数量
12     t=threading.Thread(target=foo,args=( ))     #创建对象线程
13     t.start()       #启动对象线程

Lock与Rlock的区别

这两种锁的主要区别是:RLock允许在同一线程中被多次acquire。而Lock却不允许这种情况。注意:如果使用RLock,那么acquire和release必须成对出现,即调用了n次acquire,必须调用n次的release才能真正释放所占用的琐。

时间: 2024-10-14 07:25:12

走入计算机的第三十六天(自己创建锁及锁的类型)的相关文章

走入计算机的第十五天(python基础三)

一 变量值之类型与对象程序中需要处理的状态很多,于是有了不同类型的变量值,x='egon',变量值'egon'存放与内存中,绑定一个名字x,变量值即我们要存储的数据. 在python中所有数据都是围绕对象这个概念来构建的,对象包含一些基本的数据类型:数字,字符串,列表,元组,字典等程序中存储的所有数据都是对象,一个对象(如a=1)有: 一个身份(id) 一个类型(type) 一个值(通过变量名a来查看) 二 可变对象与不可变对象    实例被创建后,身份和类型是不可变的,    如果值是不可以被

MySQL学习之——锁(行锁、表锁、页锁、乐观锁、悲观锁等)

锁,在现实生活中是为我们想要隐藏于外界所使用的一种工具.在计算机中,是协调多个进程或县城并发访问某一资源的一种机制.在数据库当中,除了传统的计算资源(CPU.RAM.I/O等等)的争用之外,数据也是一种供许多用户共享访问的资源.如何保证数据并发访问的一致性.有效性,是所有数据库必须解决的一个问题,锁的冲突也是影响数据库并发访问性能的一个重要因素.从这一角度来说,锁对于数据库而言就显得尤为重要. MySQL锁 相对于其他的数据库而言,MySQL的锁机制比较简单,最显著的特点就是不同的存储引擎支持不

MySQL锁(行锁、表锁、页锁、乐观锁、悲观锁等)

锁,在现实生活中是为我们想要隐藏于外界所使用的一种工具.在计算机中,是协调多个进程或县城并发访问某一资源的一种机制.在数据库当中,除了传统的计算资源(CPU.RAM.I/O等等)的争用之外,数据也是一种供许多用户共享访问的资源.如何保证数据并发访问的一致性.有效性,是所有数据库必须解决的一个问题,锁的冲突也是影响数据库并发访问性能的一个重要因素.从这一角度来说,锁对于数据库而言就显得尤为重要. MySQL锁 相对于其他的数据库而言,MySQL的锁机制比较简单,最显著的特点就是不同的存储引擎支持不

python网络编程--线程(锁,GIL锁,守护线程)

1.线程 1.进程与线程 进程有很多优点,它提供了多道编程,让我们感觉我们每个人都拥有自己的CPU和其他资源,可以提高计算机的利用率.很多人就不理解了,既然进程这么优秀,为什么还要线程呢?其实,仔细观察就会发现进程还是有很多缺陷的,主要体现在两点上: 进程只能在一个时间干一件事,如果想同时干两件事或多件事,进程就无能为力了. 进程在执行的过程中如果阻塞,例如等待输入,整个进程就会挂起,即使进程中有些工作不依赖于输入的数据,也将无法执行. 如果这两个缺点理解比较困难的话,举个现实的例子也许你就清楚

[转]数据库并发控制 乐观锁,悲观锁

在数据库中,并发控制有乐观锁和悲观锁之间,什么时候用乐观锁比较好什么时候用悲观锁比较好? 实际生产环境里边,如果并发量不大,完全可以使用悲观锁定的方法,这种方法使用起来非常方便和简单.但是如果系统的并发非常大的话,悲观锁定会带来非常大的性能问题,所以就要选择乐观锁定的方法. 悲观锁假定其他用户企图访问或者改变你正在访问.更改的对象的概率是很高的,因此在悲观锁的环境中,在你开始改变此对象之前就将该对象锁住,并且直到你提交了所作的更改之后才释放锁.悲观的缺陷是不论是页锁还是行锁,加锁的时间可能会很长

python--同步锁/递归锁/协程

同步锁/递归锁/协程 1 同步锁 锁通常被用来实现对共享资源的同步访问,为每一个共享资源创建一个Lock对象,当你需需要访问该资源时,调用acquire()方法来获取锁对象(如果其他线程已经获得了该锁,则当前线程需要等待其被释放),待资源访问完后,在调用release方式释放锁: import threading import time def subnum(): global num # num-=1 lock.acquire() #对用户进行加锁处理 #加锁只对用户数据 等第一个释放完之后才

可重入锁 公平锁 读写锁

1.可重入锁 如果锁具备可重入性,则称作为可重入锁. ========================================== (转)可重入和不可重入 2011-10-04 21:38 这种情况出现在多任务系统当中,在任务执行期间捕捉到信号并对其进行处理时,进程正在执行的指令序列就被信号处理程序临时中断.如果从信号处理程序返回,则继续执行进程断点处的正常指令序列,从重新恢复到断点重新执行的过程中,函数所依赖的环境没有发生改变,就说这个函数是可重入的,反之就是不可重入的.众所周知,在进

悲观锁&乐观锁

最近意外发现之前对悲观锁乐观锁的理解有误,所以重新学习了一下. 1.悲观锁 悲观锁介绍(百科): 悲观锁,正如其名,它指的是对数据被外界(包括本系统当前的其他事务,以及来自外部系统的事务处理)修改持保守态度,因此,在整个数据处理过程中,将数据处于锁定状态.悲观锁的实现,往往依靠数据库提供的锁机制(也只有数据库层提供的锁机制才能真正保证数据访问的排他性,否则,即使在本系统中实现了加锁机制,也无法保证外部系统不会修改数据). 使用场景举例:以MySQL InnoDB为例 商品goods表中有一个字段

06 锁:可重入锁 公平锁 读写锁

1.可重入锁 如果锁具备可重入性,则称作为可重入锁. 像synchronized和ReentrantLock都是可重入锁,可重入性在我看来实际上表明了锁的分配机制: 基于线程的分配,而不是基于方法调用的分配. 举个简单的例子,当一个线程执行到某个synchronized方法时,比如说method1,而在method1中会调用另外一个synchronized方法method2, 此时线程不必重新去申请锁,而是可以直接执行方法method2. class MyClass { public synch