热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

pythongil解决_PythonGIL问题

Python‘最难’的问题——GIL问题一、什么是GILGIL(解释器全局锁)从名字上看能告诉我们很多东西,很显然,这是一个加在解释器上的全局(从解释器

Python‘最难’的问题——GIL问题

一、什么是GIL

GIL(解释器全局锁)

从名字上看能告诉我们很多东西,很显然,这是一个加在解释器上的全局(从解释器的角度看)锁(从互斥或者类似角度看)。

首先来看回顾一下什么是锁:

为什么加锁

由于多线程共享进程的资源和地址空间,因此,在对这些公共资源进行操作时,为了防止这些公共资源出现异常的结果,必须考虑线程的同步和互斥问题。

加锁的作用

1、用于非线程安全,2、控制一段代码,确保其不产生调度混乱。

GIL官方给出的解释

In CPython, the global interpreter lock, or GIL, is a mutex that prevents multiple native threads from executing Python bytecodes at once. This lock is necessary mainly because CPython’s memory management is not thread-safe. (However, since the GIL exists, other features have grown to depend on the guarantees that it enforces.)

在CPython中,全局解释器锁(global interpreter lock, GIL)是一个互斥体,它防止多个本机线程同时执行Python字节码。这个锁是必要的,主要是因为CPython的内存管理不是线程安全的。(然而,自从GIL存在以来,其他特性已经逐渐依赖于它强制执行的保证。)

二、GIL的影响

GIL的设计缺陷

从上文的介绍和官方的定义来看,GIL就是一把全局排他锁。这种方式当然很安全,但是这对于任何Python程序来说,不管有多少的处理器,任何时候都总是只有一个线程在执行。毫无疑问全局锁的存在会对多线程的效率有不小影响。

但是我们课上讲的例子,并不是这样啊

上课的多线程例子:

from threading import Thread

import time

def task():

time.sleep(5)

def run():

t1 = Thread(target=task)

t2 = Thread(target=task)

start = time.time()

t1.start()

t1.join()

t2.start()

t2.join()

end = time.time()

print(f'Total time: {end - start}')

'''

串行结果:

Total time:10

'''

t1.start()

t2.start()

t1.join()

t2.join()

'''

并行结果:

Total time:5

'''

但是!

看看这个

from threading import Thread

import time

def counter():

# 计数到一亿

i = 0

for _ in range(100000000):

i += 1

return True

def run():

t1 = Thread(target=counter)

t2 = Thread(target=counter)

start = time.time()

t1.start()

t1.join()

t2.start()

t2.join()

end = time.time()

print(f'Total time: {end - start}')

'''

串行结果(即单线程):

Total time: 15.838918209075928

'''

t1.start()

t2.start()

t1.join()

t2.join()

'''

并行结果:

Total time: 16.79609990119934

(其实他们两个结果我跑的时候不相上下,但是也能说明问题)

'''

if __name__ == '__main__':

run()

问题来了。

为什么多线程并行比单线程慢,但是一些例子为什么多线程并行时间又更少?

刚刚也说了是因为GIL导致的,python解释器任何时候都是一个线程在执行。

一些情况下多线程并行快的原因是: 线程做的是i/o操作, 可以挂起当前线程去执行下一线程。因为遇到像 i/o操作这种 会有时间空闲情况 造成cpu闲置的情况会释放GIL

所以在python上只要在进行耗时的IO操作的时候,能释放GIL,这样也还是可以提升运行效率的。

为什么GIL有这个缺陷而不改进?

改不了!

为什么改不了

历史遗留问题:因为硬件的升级cpu单核变多核,当时python开发者为了利用多核,就出现了多线程编程,而随之带来的就是线程间数据一致性和状态同步的困难。 python开发者的解决方法就是加GIL这把大锁。

但是人们之后发现了这种多线程编程实现方式是垃圾的,想去除他,但是这时候已经发现离不开他了,大量的开发者已经重度依赖GIL了。

有了GIL的存在,python有这两个特点

1、进程可以利用多核,但是开销大。

2、多线程开销小,却无法利用多核优势。

也就是说Python中的多线程是假的多线程,Python解释器虽然可以开启多个线程,但同一时间只有一个线程能在解释器中执行,而做到这一点正是由于GIL锁的存在,它的存在使得CPU的资源同一时间只会给一个线程使用,而由于开启线程的开销小,所以多线程才能有一片用武之地,不然就真的是鸡肋了。

而python的多线程到底有没有用呢?

​有用。我们需要看任务是I/O密集型,还是计算密集型:

如果是I/O密集型任务,有再多核也没用,即能开再多进程也没用,所以我们利用python的多线程一点问题也没有;

如果是计算密集型任务,我们就直接使用多进程就可以了

如何解决GIL锁带来的问题

1、不用cpython,使用jpython(不太推荐)

2、使用多进程完成多线程任务(python专家推荐)

用multiprocess来代替Thread

multiprocess库的出现很大程度上是为了弥补thread库因为GIL而低效的缺陷。它完整的复制了一套thread所提供的接口方便迁移。唯一的不同就是它使用了多进程而不是多线程。每个进程有自己的独立的GIL,因此也不会出现进程之间的GIL争抢。

但是它的引入会增加程序实现时线程间数据通讯和同步的困难。

3、使用多线程时用c语言实现



推荐阅读
  • 本文介绍了Python爬虫技术基础篇面向对象高级编程(中)中的多重继承概念。通过继承,子类可以扩展父类的功能。文章以动物类层次的设计为例,讨论了按照不同分类方式设计类层次的复杂性和多重继承的优势。最后给出了哺乳动物和鸟类的设计示例,以及能跑、能飞、宠物类和非宠物类的增加对类数量的影响。 ... [详细]
  • 深入理解Java虚拟机的并发编程与性能优化
    本文主要介绍了Java内存模型与线程的相关概念,探讨了并发编程在服务端应用中的重要性。同时,介绍了Java语言和虚拟机提供的工具,帮助开发人员处理并发方面的问题,提高程序的并发能力和性能优化。文章指出,充分利用计算机处理器的能力和协调线程之间的并发操作是提高服务端程序性能的关键。 ... [详细]
  • Android中高级面试必知必会,积累总结
    本文介绍了Android中高级面试的必知必会内容,并总结了相关经验。文章指出,如今的Android市场对开发人员的要求更高,需要更专业的人才。同时,文章还给出了针对Android岗位的职责和要求,并提供了简历突出的建议。 ... [详细]
  • [译]技术公司十年经验的职场生涯回顾
    本文是一位在技术公司工作十年的职场人士对自己职业生涯的总结回顾。她的职业规划与众不同,令人深思又有趣。其中涉及到的内容有机器学习、创新创业以及引用了女性主义者在TED演讲中的部分讲义。文章表达了对职业生涯的愿望和希望,认为人类有能力不断改善自己。 ... [详细]
  • Android Studio Bumblebee | 2021.1.1(大黄蜂版本使用介绍)
    本文介绍了Android Studio Bumblebee | 2021.1.1(大黄蜂版本)的使用方法和相关知识,包括Gradle的介绍、设备管理器的配置、无线调试、新版本问题等内容。同时还提供了更新版本的下载地址和启动页面截图。 ... [详细]
  • 使用Ubuntu中的Python获取浏览器历史记录原文: ... [详细]
  • 图解redis的持久化存储机制RDB和AOF的原理和优缺点
    本文通过图解的方式介绍了redis的持久化存储机制RDB和AOF的原理和优缺点。RDB是将redis内存中的数据保存为快照文件,恢复速度较快但不支持拉链式快照。AOF是将操作日志保存到磁盘,实时存储数据但恢复速度较慢。文章详细分析了两种机制的优缺点,帮助读者更好地理解redis的持久化存储策略。 ... [详细]
  • 基于事件驱动的并发编程及其消息通信机制的同步与异步、阻塞与非阻塞、IO模型的分类
    本文介绍了基于事件驱动的并发编程中的消息通信机制,包括同步和异步的概念及其区别,阻塞和非阻塞的状态,以及IO模型的分类。同步阻塞IO、同步非阻塞IO、异步阻塞IO和异步非阻塞IO等不同的IO模型被详细解释。这些概念和模型对于理解并发编程中的消息通信和IO操作具有重要意义。 ... [详细]
  • Oracle分析函数first_value()和last_value()的用法及原理
    本文介绍了Oracle分析函数first_value()和last_value()的用法和原理,以及在查询销售记录日期和部门中的应用。通过示例和解释,详细说明了first_value()和last_value()的功能和不同之处。同时,对于last_value()的结果出现不一样的情况进行了解释,并提供了理解last_value()默认统计范围的方法。该文对于使用Oracle分析函数的开发人员和数据库管理员具有参考价值。 ... [详细]
  • 计算机存储系统的层次结构及其优势
    本文介绍了计算机存储系统的层次结构,包括高速缓存、主存储器和辅助存储器三个层次。通过分层存储数据可以提高程序的执行效率。计算机存储系统的层次结构将各种不同存储容量、存取速度和价格的存储器有机组合成整体,形成可寻址存储空间比主存储器空间大得多的存储整体。由于辅助存储器容量大、价格低,使得整体存储系统的平均价格降低。同时,高速缓存的存取速度可以和CPU的工作速度相匹配,进一步提高程序执行效率。 ... [详细]
  • CentOS 7部署KVM虚拟化环境之一架构介绍
    本文介绍了CentOS 7部署KVM虚拟化环境的架构,详细解释了虚拟化技术的概念和原理,包括全虚拟化和半虚拟化。同时介绍了虚拟机的概念和虚拟化软件的作用。 ... [详细]
  • Java在运行已编译完成的类时,是通过java虚拟机来装载和执行的,java虚拟机通过操作系统命令JAVA_HOMEbinjava–option来启 ... [详细]
  • 本文介绍了操作系统的定义和功能,包括操作系统的本质、用户界面以及系统调用的分类。同时还介绍了进程和线程的区别,包括进程和线程的定义和作用。 ... [详细]
  • 恶意软件分析的最佳编程语言及其应用
    本文介绍了学习恶意软件分析和逆向工程领域时最适合的编程语言,并重点讨论了Python的优点。Python是一种解释型、多用途的语言,具有可读性高、可快速开发、易于学习的特点。作者分享了在本地恶意软件分析中使用Python的经验,包括快速复制恶意软件组件以更好地理解其工作。此外,作者还提到了Python的跨平台优势,使得在不同操作系统上运行代码变得更加方便。 ... [详细]
  • Mono为何能跨平台
    概念JIT编译(JITcompilation),运行时需要代码时,将Microsoft中间语言(MSIL)转换为机器码的编译。CLR(CommonLa ... [详细]
author-avatar
平凡小迪
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有