python 深入了解GIL锁详细

Python深入了解GIL锁

什么是GIL锁?

GIL全局解释器锁(Global Interpreter Lock),是Python解释器的一种机制。在Python多线程执行的时候,由于GIL锁的存在,同一时间只有一个线程能够执行,其他的线程只能在等待队列中等待。

GIL锁的存在意义

在解释Python代码的时候,Python会将解释器内存中的字节码编译成对底层处理器指令的解释器程序。由于Python具有便捷易用的GIL(Global Interpreter Lock)线程锁,这意味着在任何时候只能有一个线程从你的程序中运行Python代码。

这个锁的存在使得Python的多线程执行得以实现,但在某些情况下也可能会阻止Python的多线程执行。

Python实现线程的方式

Python实现线程的方式有两种:Thread对象和对于多线程执行优化的Thread Pool

  • Thread对象:主要用于短期的线程调用,例如APIs,即在你使用APIs访问外部资源的时候,Python将会使用线程调用,但是在执行你的应用程序的主体时依旧必须采用单线程执行方式。

  • Thread Pool:因为它们复用现有的线程对象,所以与“Thread”对象相比,这种执行方法在处理长时间运行的操作(例如数据I/O)方面更加优越

GIL锁的优劣

优势:

  • GIL锁让对于Python 的多线程执行的管理变得更加简单而可行。

  • GIL锁可以实际上确保Python代码的安全性。

弊端:

  • 在“密集计算”(CPU-bound)情况下,Python 使用线程模型可能会在运行时花费更多的时间。

  • 在“io处理”(I/o-bound)情况下,Python使用线程时,多线程执行的效率可能会比单线程略快,但是使用100个线程通常比使用10个线程要慢得多!

示例说明

为了更好地理解Python GIL锁的机制,下面的示例说明将创建两个线程并测量它们在同时运行时的效率。

import threading
from datetime import datetime

def job(n):
    print(f'{n} starts!')
    for i in range(n):
        pass
    print(f'{n} ends!')

start_time = datetime.now()

# 创建两个线程
t1 = threading.Thread(target=job, args=(100000000,))
t2 = threading.Thread(target=job, args=(100000000,))

# 启动两个线程
t1.start()
t2.start()

# 等待两个线程结束
t1.join()
t2.join()

end_time = datetime.now()
print(f'Time cost: {end_time - start_time}')

运行结果如下:

1 starts!
2 starts!
1 ends!
2 ends!
Time cost: 0:00:04.104312

我们可以看到,尽管我们使用了两个线程,但是它们却是一个接一个地运行的。因为即使在运行多线程的情况下,Python的GIL锁仍然保证只有一个线程在执行。

现在我们再看一个I/O密集型的例子,使用Python的concurrent.futures模块中的ThreadPoolExecutor类实现多线程执行。以下代码从50个网站下载图标,并测量了每次请求的时间和总时间。

import requests
from concurrent.futures import ThreadPoolExecutor
from datetime import datetime

urls = [
    'https://www.github.com/favicon.ico',
    'https://www.python.org/favicon.ico',
    'http://www.cnblogs.com/favicon.ico',
    'http://c.biancheng.net/favicon.ico',
    'https://www.baidu.com/favicon.ico',
    'https://www.sina.com.cn/favicon.ico',
    'http://www.tmall.com/favicon.ico',
    'https://www.jd.com/favicon.ico',
    'http://www.pku.edu.cn/favicon.ico',
    'http://www.fudan.edu.cn/favicon.ico'
]

def download_one(url):
    img = requests.get(url)
    print(f'Download from {url}, and save it.')
    return url

start_time = datetime.now()

with ThreadPoolExecutor(max_workers=5) as executor:
    for url in urls:
        executor.submit(download_one, url)

end_time = datetime.now()
print(f'Time cost: {end_time - start_time}')

运行结果如下:

Download from http://www.cnblogs.com/favicon.ico, and save it.
Download from https://www.baidu.com/favicon.ico, and save it.
Download from https://www.jd.com/favicon.ico, and save it.
Download from https://www.github.com/favicon.ico, and save it.
Download from http://c.biancheng.net/favicon.ico, and save it.
Download from http://www.tmall.com/favicon.ico, and save it.
Download from https://www.sina.com.cn/favicon.ico, and save it.
Download from https://www.python.org/favicon.ico, and save it.
Download from http://www.fudan.edu.cn/favicon.ico, and save it.
Download from http://www.pku.edu.cn/favicon.ico, and save it.
Time cost: 0:00:01.405767

我们可以看到,这个例子中多线程的效率比单线程快得多,原因是因为它是一个I/O密集型操作,而不是一个CPU密集型操作。在这种情况下,Python多线程执行的效率通常比单线程更高。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:python 深入了解GIL锁详细 - Python技术站

(0)
上一篇 2023年5月22日
下一篇 2023年5月22日

相关文章

  • C语言 完整游戏项目坦克大战详细代码

    首先,这篇文章介绍了一个完整游戏项目坦克大战的详细代码。坦克大战是一个经典的双人游戏,玩家可以控制自己的坦克通过射击、躲避敌方坦克、摧毁敌方基地等方式获得胜利。本文详细地介绍了该游戏的 C 语言代码实现过程,包括游戏界面的设计、坦克、子弹、道具的实现、敌方 AI 的设计以及游戏结束的处理等内容。 在这篇文章中,代码示例是非常重要的,它能够直观地展示程序的实现…

    C 2023年5月24日
    00
  • C++高精度算法的使用场景详解

    C++高精度算法的使用场景详解 什么是高精度算法 高精度算法是指一种可以处理大数的算法。它是在计算机科学领域中的一种重要算法,可以解决一些需要精度极高的问题,如加密等。在 C++ 中,我们可以使用字符串来表示大数,然后通过基本的字符串操作实现高精度运算。 使用场景 高精度算法适用于处理数据量较大的问题,如以下场景: 1. 大数运算 在普通算法中,如果数据太大…

    C 2023年5月22日
    00
  • 关于 Python json中load和loads区别

    关于 Python json中load和loads区别 在Python中使用Json模块解析Json时,经常会用到json.load()和json.loads(),这两个方法都可以将Json格式的字符串转化为Python能够识别的对象,但是它们却有一定的区别。 1. json.load() json.load()方法可以从一个文件对象中读取数据,并将其解析为…

    C 2023年5月23日
    00
  • springboot项目数据库密码如何加密

    首先,为了保证数据库密码的安全性,我们可以在SpringBoot项目中使用加密算法对数据库密码进行加密。以下是实现步骤: 1.引入依赖 在项目的pom.xml文件中引入Jasypt的依赖: <dependency> <groupId>com.github.ulisesbocchio</groupId> <artifa…

    C 2023年5月23日
    00
  • jQuery实现的一个自定义Placeholder属性插件

    下面是详细的jQuery实现自定义Placeholder属性插件的攻略。 什么是Placeholder? Placeholder是HTML5新增的一个属性,可以用于在input输入框中显示提示信息。它可以在输入框为空的时候显示提示文字,当用户输入文字时,提示文字就会消失。 但是早期的浏览器并不支持该属性,因此我们需要一个jQuery插件来实现Placehol…

    C 2023年5月22日
    00
  • C++ set到底是什么

    C++的set是一个标准库中的容器,提供了有序关联容器的功能。一个set中的元素是按照特定的顺序排列的,并且每个元素只能在set中出现一次,而且这个元素的值可以作为关键字来使用。下面我们将详细介绍set的主要功能和应用。 1. set的定义和基本操作 定义一个set 定义一个set需要包含头文件<set>,同时set是一个泛型类,可以为任意类型定…

    C 2023年5月23日
    00
  • C++中新手容易犯的十种编程错误汇总

    C++中新手容易犯的十种编程错误汇总 作为一种流行的编程语言,C++在实际应用中被广泛使用,然而,对于初学者来说,C++的语法结构、编程范式以及一些细节问题容易让他们犯错。 下面列出了C++中新手容易犯的十种编程错误并提供了攻略,供初学者参考: 1. 没有包含必要的头文件 C++中有大量的头文件,而这些头文件中定义了许多有用的函数和变量。如果没有包含必要的头…

    C 2023年5月22日
    00
  • C++内核对象封装单实例启动程序的类

    针对这个话题,我来给你详细讲解一下。 什么是C++内核对象封装单实例启动程序的类 C++内核对象封装单实例启动程序的类,是一种用C++编写的程序类,可以确保只有一个实例被启动运行,防止多次启动同一程序时造成的冲突和不必要的资源浪费。该类通常会使用操作系统的内核对象来进行进程管理和控制,保证只有一个实例在运行。 如何实现C++内核对象封装单实例启动程序的类 下…

    C 2023年5月22日
    00
合作推广
合作推广
分享本页
返回顶部