python 多线程锁机制

GIL(全局解释器锁)

GIL并不是Python的特性,它是在实现Python解析器(CPython)时所引入的一个概念,是为了实现不同线程对共享资源访问的互斥,才引入了GIL

在Cpython解释器中,同一个进程下开启的多线程,同一时刻只能有一个线程执行,无法利用多核优势

python对于计算密集型的任务开多线程的效率甚至不如串行(没有大量切换),但是,对于IO密集型的任务效率还是有显著提升的。

GIL原理图

计算密集型:结果肯定是100,因为每一次start结果就已经出来了,所以第二个线程肯定是通过调用第一个线程的count值进行计算的

 1 def sub():
 2     global count
 3
 4     ‘‘‘线程的公共数据  下‘‘‘
 5     temp=count
 6     count=temp+1
 7     ‘‘‘线程的公共数据  上‘‘‘
 8
 9     time.sleep(2)
10 count=0
11
12 l=[]
13 for i in range(100):
14     t=threading.Thread(target=sub,args=())
15     t.start()  #每一次线程激活,申请一次gillock
16     l.append(t)
17 for t in l:
18     t.join()
19 print(count)

io密集型:当第一个线程开始start的时候,由于sleep了0.001秒,这0.001秒对于人而言很短,但是对于cpu而言,这0.001秒已经做了很多的事情了,在这里cpu做的事情就是或许已经start了100个线程,所以导致大多数的线程调用的count值还是0,即temp=0,只有少数的线程完成了count=temp+1的操作,所以输出的count结果不确定,可能是7、8、9,也可能是10几。

 1 def sub():
 2     global count
 3
 4     ‘‘‘线程的公共数据  下‘‘‘
 5     temp=count
 6     time.sleep(0.001)    #大量的io操作
 7     count=temp+1
 8     ‘‘‘线程的公共数据  上‘‘‘
 9
10     time.sleep(2)
11 count=0
12
13 l=[]
14 for i in range(100):
15     t=threading.Thread(target=sub,args=())
16     t.start()
17     l.append(t)
18 for t in l:
19     t.join()
20 print(count)

注意以下的锁都是多线程提供的锁机制,与python解释器引入的gil概念无关

互斥锁(同步锁)

互斥锁是用来解决上述的io密集型场景产生的计算错误,即目的是为了保护共享的数据,同一时间只能有一个线程来修改共享的数据。

 1 def sub():
 2     global count
 3     lock.acquire()  #上锁,第一个线程如果申请到锁,会在执行公共数据的过程中持续阻塞后续线程
 4                     #即后续第二个或其他线程依次来了发现已经被上锁,只能等待第一个线程释放锁
 5                     #当第一个线程将锁释放,后续的线程会进行争抢
 6
 7     ‘‘‘线程的公共数据  下‘‘‘
 8     temp=count
 9     time.sleep(0.001)
10     count=temp+1
11     ‘‘‘线程的公共数据  上‘‘‘
12
13     lock.release()  #释放锁
14     time.sleep(2)
15 count=0
16
17 l=[]
18 lock=threading.Lock()   #将锁内的代码串行化
19 for i in range(100):
20     t=threading.Thread(target=sub,args=())
21     t.start()
22     l.append(t)
23 for t in l:
24     t.join()
25 print(count)

死锁

保护不同的数据就应该加不同的锁。

所以当有多个互斥锁存在的时候,可能会导致死锁,死锁原理如下:

 1 import threading
 2 import time
 3 def foo():
 4     lockA.acquire()
 5     print(‘func foo ClockA lock‘)
 6     lockB.acquire()
 7     print(‘func foo ClockB lock‘)
 8     lockB.release()
 9     lockA.release()
10
11 def bar():
12
13     lockB.acquire()
14     print(‘func bar ClockB lock‘)
15     time.sleep(2)  # 模拟io或者其他操作,第一个线程执行到这,在这个时候,lockA会被第二个进程占用
16                    # 所以第一个进程无法进行后续操作,只能等待lockA锁的释放
17     lockA.acquire()
18     print(‘func bar ClockA lock‘)
19     lockB.release()
20     lockA.release()
21
22 def run():
23     foo()
24     bar()
25
26 lockA=threading.Lock()
27 lockB=threading.Lock()
28 for i in range(10):
29     t=threading.Thread(target=run,args=())
30     t.start()
31
32 输出结果:只有四行,因为产生了死锁阻断了
33 func foo ClockA lock
34 func foo ClockB lock
35 func bar ClockB lock
36 func foo ClockA lock

递归锁(重要)

解决死锁

 1 import threading
 2 import time
 3 def foo():
 4     rlock.acquire()
 5     print(‘func foo ClockA lock‘)
 6     rlock.acquire()
 7     print(‘func foo ClockB lock‘)
 8     rlock.release()
 9     rlock.release()
10
11 def bar():
12     rlock.acquire()
13     print(‘func bar ClockB lock‘)
14     time.sleep(2)
15     rlock.acquire()
16     print(‘func bar ClockA lock‘)
17     rlock.release()
18     rlock.release()
19
20
21 def run():
22     foo()
23     bar()
24
25 rlock=threading.RLock() #RLock本身有一个计数器,如果碰到acquire,那么计数器+1
26                         #如果计数器大于0,那么其他线程无法查收,如果碰到release,计数器-1
27
28 for i in range(10):
29     t=threading.Thread(target=run,args=())
30     t.start()

Semaphore(信号量)

实际上也是一种锁,该锁用于限制线程的并发量

以下代码在sleep两秒后会打印出100个ok

1 import threading
2 import time
3 def foo():
4     time.sleep(2)
5     print(‘ok‘)
6
7 for i in range(100):
8     t=threading.Thread(target=foo,args=())
9     t.start()

每2秒打印5次ok

 1 import threading
 2 import time
 3 sem=threading.Semaphore(5)
 4 def foo():
 5     sem.acquire()
 6     time.sleep(2)
 7     print(‘ok‘)
 8     sem.release()
 9
10 for i in range(100):
11     t=threading.Thread(target=foo,args=())
12     t.start()

时间: 2024-08-26 13:00:32

python 多线程锁机制的相关文章

Python多线程锁

[Python之旅]第六篇(四):Python多线程锁 python lock 多线程 多线程使用方法 多线程锁 摘要:   在多线程程序执行过程中,为什么需要给一些线程加锁以及如何加锁,下面就来说一说. 1.给线程加锁的原因     我们知道,不同进程之间的内存空间数据是不能够共享的,试想一下,如果可以随意共享,谈何安全?但是一个进程中的多个线程是可以共享这个进程的内存空间中的数据的,比如多个线程可以同时调用某一... 在多线程程序执行过程中,为什么需要给一些线程加锁以及如何加锁,下面就来说一

【Python之旅】第六篇(四):Python多线程锁

    在多线程程序执行过程中,为什么需要给一些线程加锁以及如何加锁,下面就来说一说. 1.给线程加锁的原因 我们知道,不同进程之间的内存空间数据是不能够共享的,试想一下,如果可以随意共享,谈何安全?但是一个进程中的多个线程是可以共享这个进程的内存空间中的数据的,比如多个线程可以同时调用某一内存空间中的某些数据(只是调用,没有做修改). 试想一下,在某一进程中,内存空间中存有一个变量对象的值为num=8,假如某一时刻有多个线程需要同时使用这个对象,出于这些线程要实现不同功能的需要,线程A需要将n

[java多线程] - 锁机制&同步代码块&信号量

在美眉图片下载demo中,我们可以看到多个线程在公用一些变量,这个时候难免会发生冲突.冲突并不可怕,可怕的是当多线程的情况下,你没法控制冲突.按照我的理解在java中实现同步的方式分为三种,分别是:同步代码块机制,锁机制,信号量机制. 一.同步代码块 在java的多线程并发开发过程中,我们最常用的方式就是使用同步代码关键字(synchronized).这种方式的使用不是特别复杂,需要注意的只是你需要明确到底同步的是那个对象,只有当同步的对象一致的情况下,才能够控制互斥的操作.一般情况下,我们会同

python多线程同步机制Semaphore

#!/usr/bin/env python # -*- coding: utf-8 -*- """ Python 线程同步机制:Semaphore """ import time import threading import random # 信号量同步基于内部计数器,每调用一次acquire(),计数器减1:每调用一次release(),计数器加1.当计数器为0时,acquire()调用被阻塞. sema = threading.Semaph

python多线程同步机制condition

#!/usr/bin/env python# -*- coding: utf-8 -*- import threadingimport time def customer(cond): t = threading.currentThread() with cond: # wait()方法创建了一个名为waiter的锁,并且设置锁的状态为locked.这个waiter锁用于线程间的通讯 cond.wait() print '{}: Resource is available to consumer

python多线程同步机制Lock

#!/usr/bin/env python# -*- coding: utf-8 -*- import threadingimport time value = 0lock = threading.Lock() def add(): global value with lock: new_value = value + 1 time.sleep(0.001) value = new_value if __name__ == '__main__': threads = [] for i in ra

Python多线程、进程入门1

进程是资源的一个集合, 1.一个应用程序,可以有多进程和多线程 2.默认一个程序是单进程单线程 IO操作使用多线程提高并发 计算操作使用多进程提高并发 进程与线程区别 1.线程共享内存空间,进程的内存是独立的 2.线程共享进程内存的数据,进程之间的数据是无法进行访问的 3.在同一个进程内,线程之间可以直接进行数据的访问,两个进程如果通信,必须通过一个中间代理进行通信 4.创建线程非常容易,创建新进程需要对其父进程进行一次克隆 5.一个线程可以控制和操作同一进程里的其它线程,但是进程只能操作子进程

《python源码剖析》笔记 python多线程机制

本文为senlie原创,转载请保留此地址:http://blog.csdn.net/zhengsenlie 1.GIL与线程调度 Python中的线程是操作系统的原生线程,Python虚拟机使用一个全局解释器锁(Global Interpreter Lock)来互斥线程对Python虚拟机的使用 为了支持多线程机制,一个基本的要求就是需要实现不同线程对共享资源访问的互斥,所以引入了GIL. GIL:在一个线程拥有了解释器的访问权之后,其他的所有线程都必须等待它释放解释器的访问权,即使这些线程的下

Python多线程和Python的锁

Python多线程 Python中实现多线程有两种方式,一种基于_thread模块(在Python2.x版本中为thread模块,没有下划线)的start_new_thread()函数,另一种基于threading模块的Thread类. 其实Python的多线程编程不能真正利用多核的CPU,但是用开源模块使你的计算压力分布到多核CPU上......... 一.使用start_new_thread()实现线程,是比较底层的实现方式,所有线程共享他们global数据,为了达到同步,模块也提供了简单的