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()

python基础之多线程锁机制的更多相关文章

  1. Python开发基础-Day30多线程锁机制

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

  2. python基础-12 多线程queue 线程交互event 线程锁 自定义线程池 进程 进程锁 进程池 进程交互数据资源共享

    Python中的进程与线程 学习知识,我们不但要知其然,还是知其所以然.你做到了你就比别人NB. 我们先了解一下什么是进程和线程. 进程与线程的历史 我们都知道计算机是由硬件和软件组成的.硬件中的CP ...

  3. python 多线程锁机制

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

  4. python基础之多线程与多进程(二)

    上课笔记整理: 守护线程的作用,起到监听的作用 一个函数连接数据库 一个做守护线程,监听日志 两个线程同时取一个数据 线程---->线程安全---->线程同时进行操作数据. IO操作--- ...

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

    在美眉图片下载demo中,我们可以看到多个线程在公用一些变量,这个时候难免会发生冲突.冲突并不可怕,可怕的是当多线程的情况下,你没法控制冲突.按照我的理解在java中实现同步的方式分为三种,分别是:同 ...

  6. Python高阶之多线程锁机制

    '''1.多进程的优势:为了同步完成多项任务,通过提高资源使用效率来提高系统的效率.2.查看线程数:threading.enumerate()函数便可以看到当前线程的数量.3.查看当前线程的名字:th ...

  7. python基础之多线程与多进程(一)

    并发编程? 1.为什么要有操作系统? 操作系统,位于底层硬件与应用软件之间 工作方式:向下管理硬件,向上提供接口 2.多道技术? 不断切换程序. 操作系统进程切换: 1.出现IO操作 2.固定时间 进 ...

  8. Python学习 :多线程 --- 锁

    多线程 什么是锁? - 锁通常被用来实现对共享资源的同步访问. - 为每一个共享资源创建一个Lock对象,当你需要访问该资源时,调用acquire方法来获取锁对象(如果其它线程已经获得了该锁,则当前线 ...

  9. 【java基础】Java锁机制

    在读很多并发文章中,会提及各种各样锁如公平锁,乐观锁等等,这篇文章介绍各种锁的分类.介绍的内容如下: 公平锁/非公平锁 可重入锁 独享锁/共享锁(广义) 互斥锁/读写锁(独享锁/共享锁的实现) 乐观锁 ...

随机推荐

  1. iOS事件拦截(实现触摸任意位置隐藏指定view)

    项目里有一个需求,类似新浪或者腾讯微博的顶部title栏的类别选择器的消失(在选择器展开的时候,触摸屏幕任何地方使其消失). 最开始的想法是当这个选择器(selectorView)展开的时候,在当前屏 ...

  2. 关于Ethread的一些研究

    环境 win764 以TP为例 ring3保护 它会在windbg断下 这个时候我们需要拿到当前线程对象 应该到 当前使用的CPU的地址 _KPRCB-> CurrentThread 就是当前线 ...

  3. 标准web浏览器的组件

    浏览器基本上包括如下几个组件 1.HTML.XML.CSS.JavsScript解析器 2.Layout 3.文字和图形渲染 4.图像解码 5.GPU交互 6.网络访问 7.硬件加速

  4. IE8及以下的数组处理与其它浏览器的不同

    在解决search-box的bug时,由于IE8-的数组处理与其它浏览器的不同,而导致报错. 示例:arr=[1,3,3,]; 当数组的最后是一个逗号时: IE9+默认 arr=[1,3,3];也就是 ...

  5. 【BZOJ2599】[IOI2011]Race 树的点分治

    [BZOJ2599][IOI2011]Race Description 给一棵树,每条边有权.求一条简单路径,权值和等于K,且边的数量最小.N <= 200000, K <= 100000 ...

  6. 百度地图api添加自定义控件

    官网栗子:http://lbsyun.baidu.com/jsdemo.htm#b0_6 <!DOCTYPE html><html><head> <meta ...

  7. 模拟退火算法(西安网选赛hdu5017)

    Ellipsoid Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Total ...

  8. 编程中,static的用法详解

    C++的static有两种用法:面向过程程序设计中的static和面向对象程序设计中的static.前者应用于普通变量和函数,不涉及类:后者主要说明static在类中的作用.一.面向过程设计中的sta ...

  9. JavaMVC框架之SpringMVC

    欢迎查看Java开发之上帝之眼系列教程,如果您正在为Java后端庞大的体系所困扰,如果您正在为各种繁出不穷的技术和各种框架所迷茫,那么本系列文章将带您窥探Java庞大的体系.本系列教程希望您能站在上帝 ...

  10. 扫描类APP推荐

    扫描全能王 (com.intsig.camscanner) - 5.10.0.20190426 - 应用 - 酷安网 应该是手机上最好的扫描类 APP 了,没有之一.只是因为付费太贵. 感谢酷安评论区 ...