Python Threading 学习笔记 | 5、不一定有效率GIL

0x00 关于GIL

GIL的全称是Global Interpreter Lock(全局解释器锁),来源是python设计之初的考虑,为了数据安全所做的决定。

每个CPU在同一时间只能执行一个线程(在单核CPU下的多线程其实都只是并发,不是并行,并发和并行从宏观上来讲都是同时处理多路请求的概念。但并发和并行又有区别,并行是指两个或者多个事件在同一时刻发生;而并发是指两个或多个事件在同一时间间隔内发生。)

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

1.获取GIL

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

3.释放GIL

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

也就是说尽管Python支持多线程,但是因为GIL的存在,使得Python还是一次性只能处理一个东西,那是不是说Python中的多线程就完全没用了呢,当然不是的。

GIL往往只会影响到那些严重依赖CPU的程序,比如各种循环处理、计数等这种CPU密集型的程序;如果程序中大部分只会涉及到I/O,比如文件处理、网络爬虫等这种IO密集型的程序,那么多线程就能够有效的提高效率,因为在爬虫的时候大部分时间都在等待。

实际上,你完全可以放心的创建几千个Python线程, 现代操作系统运行这么多线程没有任何压力,没啥可担心的。

0x01 测试GIL

import copy
import time
import requests
import threading
from queue import Queue

def job(lists,q):
   res = sum(lists)
   q.put(res)


def multithreading(lists):
   q = Queue()
   threads_list = []

   for i in range(4):
      t = threading.Thread(target=job,args=(copy.copy(lists),q),name = '任务 %i' % i)
      t.start()
      threads_list.append(t)
   for t in threads_list:
      t.join()

   total = 0
   for _ in range(4):
      total += q.get()
   print('使用线程运算结果:',total)


def normal(lists):
   total = sum(lists)
   print('不使用线程运算结果:',total)


def req_job(i):
   requests.get(i)


def req_multithreading(req_lists):
   threads_list = []

   for i in range(4):
      t = threading.Thread(target=req_job,args=(req_lists[i],),name='爬虫任务 %i' % i)
      t.start()
      threads_list.append(t)
   for t in threads_list:
      t.join()


def req_normal(req_lists):
   for i in req_lists:
      requests.get(i)


if __name__ == '__main__':
   lists = list(range(1000000)) # 完成一个较大的计算
   req_lists = ['https://www.teamssix.com','https://github.com/teamssix','https://me.csdn.net/qq_37683287','https://space.bilibili.com/148389186']
   start_time = time.time()
   multithreading(lists)
   print('计算使用线程耗时:', time.time() - start_time,'\n')

   start_time = time.time()
   normal(lists * 4)
   print('计算不使用线程耗时:', time.time() - start_time,'\n')
   
   start_time = time.time()
   req_multithreading(req_lists)
   print('爬虫使用线程耗时:', time.time() - start_time)
   
   start_time = time.time()
   req_normal(req_lists)
   print('爬虫不使用线程耗时:', time.time() - start_time)

运行结果:

# python 5_GIL.py
使用线程运算结果: 1999998000000
计算使用线程耗时: 0.39594030380249023 

不使用线程运算结果: 1999998000000
计算不使用线程耗时: 0.3919515609741211 

爬虫使用线程耗时: 2.2410056591033936
爬虫不使用线程耗时: 7.1159656047821045

可以看到在计算程序的代码中不使用线程和使用线程的运算结果是相同的,说明不使用线程和使用线程的程序都进行了一样多次的运算,但是很明显可以看到计算的耗时并没有少很多,按照预期我们使用了4个线程,应该会快近4倍才对,这就是因为GIL在作怪。

与此同时,可以看到在使用request对一个url发起get请求的时候,使用线程比不使用线程快了3倍多,也进一步的反映出在使用Python进行爬虫的时候,多线程确实可以很大程度上提高效率,但是在进行密集计算任务的时候,多线程就显得很鸡肋了。

代码项目地址:https://github.com/teamssix/Python-Threading-study-notes 参考文章: 1、https://zhuanlan.zhihu.com/p/20953544 2、https://morvanzhou.github.io/tutorials/python-basic/threading

本文分享自微信公众号 - TeamsSix(OldCat0111)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-11-04

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏测试游记

测试开发进阶(二十五)

LearnDjango/settings.py中添加 'rest_framework'

8630
来自专栏量化小白上分记

符号回归和遗传规划

回归分析是一种常用的统计方法,用来分析自变量和因变量的线性相关关系,在线性回归分析中,变量间的关系形式是确定的,只需要对关系式的系数做出估计。

13610
来自专栏用户5521492的专栏

Python | Python 的 is 和 == 你了解么?

其中 id 也代表着内存地址,Python 对象之间比较是否相等既可以用 == ,也可以用 is ,那么二者的区别在哪呢?

12920
来自专栏编程创造城市

Python高级进阶#007 pyqt5消息盒子QMessageBox

带有图标的消息盒子,图标可以是问号question,信息information,警告warning

11040
来自专栏7DGroup

性能监控之Telegraf+InfluxDB+Grafana+Python实现Oracle实时监控

通过查询 V$FIXED_TABLE ,可以列出所有可用的动态性能视图和动态性能表。

19250
来自专栏编程创造城市

零基础html5+div+css+js网页开发教程第003期 html代码基本结构

在第二期中,我们对html做了入门,已经有了对网页开发基本的了解。本节知识开始书写html网页结构。

12730
来自专栏用户5521492的专栏

「 计算机视觉」帧差法移动侦测

这是我大学舍友小钊在前几天培训公司内部人员顺便记录的一篇关于帧差法移动侦测的文章,介绍下小钊,广西佬,我的大学舍友,特点是烟勤话少爱抠脚。是一名计算机视觉工程师...

8720
来自专栏用户5521492的专栏

Python | 50行代码爬取猫眼 top100

好久不见,已经有一个月没更新了。主要是因为最近工作特别忙,上班要撸 java, 撸完 java,又要撸前端。真的是忙到 x 生活都没时间(说的好像不忙就有一样)...

20230
来自专栏用户5521492的专栏

聊聊 Python 字符串连接的七种方式

我是狗哥,一名程序猿。做过 Android、撸过 Java、目前在自学 Python 。注册 「一个优秀的废人」这个公号已有些日子,真正有心将它运营起来是这两天...

10830
来自专栏APP测试

print,Python旅程开始的地方!

通过print('HelloWorld'),Python为你打开一扇门,你已经轻轻地推开了Python世界的大门。

11020

扫码关注云+社区

领取腾讯云代金券

年度创作总结 领取年终奖励