Python全局锁中如何合理运用多线程(多进程)


Posted in Python onNovember 06, 2019

Python全局锁

(1)全局锁导致的问题

全局锁的英文简称是GIL,全称是Global Interpreter Lock(全局解释器锁),来源是python设计之初的考虑,为了数据安全所做的决定,每个线程在执行时候都需要先获取GIL,保证同一时刻只有一个线程可以执行代码,即同一时刻只有一个线程使用CPU,也就是说多线程并不是真正意义上的同时执行。
每个CPU在同一时间只能执行一个线程(在单核CPU下的多线程其实都只是并发,不是并行,并发和并行从宏观上来讲都是同时处理多路请求的概念。但并发和并行又有区别,并行是指两个或者多个事件在同一时刻发生(多个CPU同时执行某个任务);而并发是指两个或多个事件在同一时间间隔内发生。)

在Python多线程下,每个线程的执行方式:

1、获取GIL

2、执行代码直到sleep或者是python虚拟机将其挂起。

3、释放GIL

可见,某个线程想要执行,必须先拿到GIL,我们可以把GIL看作是“通行证”,并且在一个python进程中,GIL只有一个。拿不到通行证的线程,就不允许进入CPU执行。

在Python2.x里,GIL的释放逻辑是当前线程遇见IO操作或者ticks计数达到100(ticks可以看作是Python自身的一个计数器,专门做用于GIL,每次释放后归零,这个计数可以通过 sys.setcheckinterval 来调整),进行释放。
而每次释放GIL锁,线程进行锁竞争、切换线程,会消耗资源。并且由于GIL锁存在,python里一个进程永远只能同时执行一个线程(拿到GIL的线程才能执行),这就是为什么在多核CPU上,python的多线程效率并不高。

(2)在有全局锁的情况下如何运行多线程、多进程

在这里我们进行分类讨论:

1、CPU密集型代码(各种循环处理、计数等等),在这种情况下,由于计算工作多,ticks计数很快就会达到阈值,然后触发GIL的释放与再竞争(多个线程来回切换当然是需要消耗资源的),所以python下的多线程对CPU密集型代码并不友好,此时可以采用多进程形式实现多任务。

2、IO密集型代码(文件处理、网络爬虫等),多线程能够有效提升效率(单线程下有IO操作会进行IO等待,造成不必要的时间浪费,而开启多线程能在线程A等待时,自动切换到线程B,可以不浪费CPU的资源,从而能提升程序执行效率)。所以python的多线程对IO密集型代码比较友好。

而在python3.x中,GIL不使用ticks计数,改为使用计时器(执行时间达到阈值后,当前线程释放GIL),这样对CPU密集型程序更加友好,但依然没有解决GIL导致的同一时间只能执行一个线程的问题,所以效率依然不尽如人意。

请注意:多核多线程比单核多线程更差,原因是单核下多线程,每次释放GIL,唤醒的那个线程都能获取到GIL锁,所以能够无缝执行,但多核下,CPU0释放GIL后,其他CPU上的线程都会进行竞争,但GIL可能会马上又被CPU0拿到,导致其他几个CPU上被唤醒后的线程会醒着等待到切换时间后又进入待调度状态,这样会造成线程颠簸(thrashing),导致效率更低

回到最开始的问题:经常我们会听到老手说:“python下想要充分利用多核CPU,就用多进程”,原因是什么呢?
原因是:每个进程有各自独立的GIL,互不干扰,这样就可以真正意义上的并行执行,所以在python中,多进程的执行效率优于多线程(仅仅针对多核CPU而言)。

(3)代码实例

使用一个线程去计数

#encoing:utf-8
import threading
import time
def test_counter():
  i = 0
  for _ in range(100000000):
    i += 1
  return True
def main():
  start_time = time.time()
  for tid in range(2):
    t1 = threading.Thread(target=test_counter)
    t1.start()
    t1.join()
  end_time = time.time()
  print("Total time:{}".format(end_time-start_time))
if __name__ == "__main__":
  main()

结果:

Python全局锁中如何合理运用多线程(多进程)

使用2个线程,去执行非IO操作

#encoding:utf-8
import threading
import time
def test_counter():
  i = 0
  for _ in range(100000000):
    i += 1
  return True
def main():
  thread_array = {}
  start_time = time.time()
  for tid in range(2):
    t = threading.Thread(target=test_counter)
    t.start()
    thread_array[tid] = t
  for i in range(2):
    thread_array[i].join()
  end_time = time.time()
  print("Total time:{}".format(end_time-start_time))
if __name__ == "__main__":
  main()

结果:

Python全局锁中如何合理运用多线程(多进程)

通过上面的代码可以得出,对于非IO类型操作,多线程为了获得GIL去相互竞争,导致程序执行效率更低,所以我们要根据实际的业务功能情况,来确定使用多线程、多进程!

总结

以上所述是小编给大家介绍的在Python全局锁中如何合理运用多线程(多进程),希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!
如果你觉得本文对你有帮助,欢迎转载,烦请注明出处,谢谢!

Python 相关文章推荐
Python实现给文件添加内容及得到文件信息的方法
May 28 Python
Python设计模式之抽象工厂模式
Aug 25 Python
Python+OpenCV实现图像融合的原理及代码
Dec 03 Python
python使用Plotly绘图工具绘制柱状图
Apr 01 Python
一文秒懂python读写csv xml json文件各种骚操作
Jul 04 Python
Windows平台Python编程必会模块之pywin32介绍
Oct 01 Python
Python字节单位转换实例
Dec 05 Python
Python动态声明变量赋值代码实例
Dec 30 Python
Pytorch 神经网络—自定义数据集上实现教程
Jan 07 Python
Python爬虫教程知识点总结
Oct 19 Python
Selenium Webdriver元素定位的八种常用方式(小结)
Jan 13 Python
Python使用PyYAML库读写yaml文件的方法
Apr 06 Python
Python实现socket非阻塞通讯功能示例
Nov 06 #Python
Python中生成一个指定长度的随机字符串实现示例
Nov 06 #Python
详解Python list和numpy array的存储和读取方法
Nov 06 #Python
python函数装饰器之带参数的函数和带参数的装饰器用法示例
Nov 06 #Python
Python list与NumPy array 区分详解
Nov 06 #Python
Django实现WebSSH操作物理机或虚拟机的方法
Nov 06 #Python
django 简单实现登录验证给你
Nov 06 #Python
You might like
php的ajax简单实例
2014/02/27 PHP
详解在YII2框架中使用UEditor编辑器发布文章
2018/11/02 PHP
document.all还是document.getElementsByName?
2006/07/21 Javascript
javascript instanceof 内部机制探析
2010/10/15 Javascript
JS俄罗斯方块,包含完整的设计理念
2010/12/11 Javascript
Dreamweaver jQuery智能提示插件,支持版本提示,支持1.6api
2011/07/31 Javascript
JS字符串截取函数实例
2013/12/27 Javascript
javascript实时获取鼠标坐标值并显示的方法
2015/04/30 Javascript
js仿苹果iwatch外观的计时器代码分享
2015/08/26 Javascript
微信小程序 toast 详解及实例代码
2016/11/09 Javascript
angularjs使用directive实现分页组件的示例
2017/02/07 Javascript
原生JS中slice()方法和splice()区别
2017/03/06 Javascript
vue项目中公用footer组件底部位置的适配问题
2018/05/10 Javascript
取消Bootstrap的dropdown-menu点击默认关闭事件方法
2018/08/10 Javascript
一篇超完整的Vue新手入门指导教程
2020/11/18 Vue.js
js实现简单的轮播图效果
2020/12/13 Javascript
[00:12]2018DOTA2亚洲邀请赛 Somnus丶M出阵单挑
2018/04/06 DOTA
[01:16:50]DOTA2-DPC中国联赛 正赛 Phoenix vs CDEC BO3 第一场 3月7日
2021/03/11 DOTA
Python分割指定页数的pdf文件方法
2018/10/26 Python
安装好Pycharm后如何配置Python解释器简易教程
2019/06/28 Python
python顺序执行多个py文件的方法
2019/06/29 Python
Python实现钉钉订阅消息功能
2020/01/14 Python
jenkins+python自动化测试持续集成教程
2020/05/12 Python
基于Pytorch版yolov5的滑块验证码破解思路详解
2021/02/25 Python
如何使用css3实现一个类在线直播的队列动画的示例代码
2020/06/17 HTML / CSS
HTML5 视频播放(video),JavaScript控制视频的实例代码
2018/10/08 HTML / CSS
html5跳转小程序wx-open-launch-weapp踩坑
2020/12/02 HTML / CSS
Puma印度官网:德国运动品牌
2019/10/06 全球购物
介绍一下如何利用路径遍历进行攻击及如何防范
2014/01/19 面试题
公司庆典活动邀请函
2014/01/09 职场文书
办公室副主任职责范本
2014/03/08 职场文书
2015年酒店前台工作总结
2015/04/20 职场文书
一个独生女的故事观后感
2015/06/04 职场文书
钓鱼岛事件感想
2015/08/11 职场文书
互联网创业商业模式以及赚钱法则有哪些?
2019/10/12 职场文书
Python list去重且保持原顺序不变的方法
2021/04/03 Python