python GIL 的多线程示例

2023-12-22

我读过很多关于在编写多线程代码时 python GIL 业务有多么“糟糕”的文章,但我从未见过一个例子。有人可以给我一个基本的例子来说明 GIL 在使用线程时何时会导致问题吗?

Thanks!


多线程的主要原因之一是程序可以利用多个 CPU(和/或 CPU 上的多个内核)来每秒计算更多操作。但在 Python 中,GIL 意味着即使有多个线程同时进行计算,在任何给定时刻实际上只有其中一个线程会运行,因为所有其他线程都将被阻塞,等待获取全局解释器锁。这意味着 Python 程序的多线程版本实际上是slower比单线程版本更快,因为一次只有一个线程运行——此外,强制每个线程每次都等待、获取然后放弃 GIL(循环方式)会产生记账开销。几毫秒。

为了演示这一点,这里有一个玩具 Python 脚本,它生成指定数量的线程,然后作为其“计算”,每个线程不断增加计数器,直到 5 秒过去。最后,主线程会计算发生的反增量总数并打印总数,以便我们衡量 5 秒期间完成了多少“工作”。

import threading
import sys
import time

numSecondsToRun = 5

class CounterThread(threading.Thread):
   def __init__(self):
      threading.Thread.__init__(self)
      self._counter = 0
      self._endTime = time.time() + numSecondsToRun

   def run(self):
      # Simulate a computation on the CPU
      while(time.time() < self._endTime):
         self._counter += 1

if __name__ == "__main__":
   if len(sys.argv) < 2:
      print "Usage:  python counter 5"
      sys.exit(5)

   numThreads = int(sys.argv[1])
   print "Spawning %i counting threads for %i seconds..." % (numThreads, numSecondsToRun)

   threads = []
   for i in range(0,numThreads):
      t = CounterThread()
      t.start()
      threads.append(t)

   totalCounted = 0
   for t in threads:
      t.join()
      totalCounted += t._counter
   print "Total amount counted was %i" % totalCounted

....以下是我在计算机(启用超线程的双核 Mac Mini,FWIW)上得到的结果:

$ python counter.py 1
Spawning 1 counting threads for 5 seconds...
Total amount counted was 14210740

$ python counter.py 2
Spawning 2 counting threads for 5 seconds...
Total amount counted was 10398956

$ python counter.py 3
Spawning 3 counting threads for 5 seconds...
Total amount counted was 10588091

$ python counter.py 4
Spawning 4 counting threads for 5 seconds...
Total amount counted was 11091197

$ python counter.py 5
Spawning 5 counting threads for 5 seconds...
Total amount counted was 11130036

$ python counter.py 6
Spawning 6 counting threads for 5 seconds...
Total amount counted was 10771654

$ python counter.py 7
Spawning 7 counting threads for 5 seconds...
Total amount counted was 10464226

请注意第一次迭代如何实现最佳性能(仅生成一个工作线程);当多个线程同时运行时,计数效率会大幅下降。这显示了 GIL 如何削弱 Python 中的多线程性能——用 C(或任何其他没有 GIL 的语言)编写的相同程序在运行更多线程时会表现出更好的性能,而不是更差(直到工作线程的数量与当然,硬件上的核心数量)。

但这并不意味着多线程在 Python 中完全无用——在大多数或所有线程被阻塞等待 I/O 而不是 CPU 限制的情况下,它仍然有用。这是因为被阻塞等待 I/O 的 Python 线程在等待时不会锁定 GIL,因此在此期间其他线程仍然可以自由执行。但是,如果您需要并行化计算密集型任务(例如光线追踪或计算 Pi 的所有数字或代码破译或类似),那么您将需要使用多个进程而不是多个线程,或者使用不同的语言没有 GIL。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

python GIL 的多线程示例 的相关文章

随机推荐

  • 在 VT100/xterm 终端(Mac OS X 的终端)中向 Emacs 发送“C-(”?

    是否可以以任何方式通过 VT100 xterm 终端 Mac OS X 终端 将键 C 发送到 Emacs 是否可以发送转义序列来实现等效功能 我怀疑根本问题在于 将控制与字符 以及使用移位生成的其他此类字符 相结合的概念不存在 注意 使用
  • 检测 DataTable 上的页面更改

    With 数据表 http datatables net 我可以排序 列出 分页 但我想检测分页何时更改 我已经看到了API http datatables net api但我唯一能做的就是更改页面但没有检测到此更改 您可以使用fnDraw
  • SQL Server 2005/2008:在 Transact-SQL 中的 varbinary(max) 列中插入文件

    是否可以在 Transact SQL 中的 varbinary max 列中插入文件 如果是 我会非常高兴有一个代码片段至少让我知道如何做到这一点 Thanks 就这么简单 一旦您知道了 发现这个格雷格 邓肯的博客 http coolthi
  • 让 Cancan 的 load_and_authorize_resource 在自定义创建操作中工作

    正在尝试设置Cancan在我的应用程序中 并且我的应用程序遇到问题PostsController 简而言之 当Post创建后我希望它与current user所以我的创建操作看起来像这样 class PostsController lt A
  • 工厂方法的命名约定是什么?

    介绍 MacApp Macintosh 应用程序框架 App89 始终声明 将工厂方法定义为 Class 的抽象操作 DoMakeClass 其中 Class 是 Product 类 这句话让我想到了关于工厂方法设计模式的命名约定的问题 期
  • fortran 中的过程 nopass 与普通函数

    因此 在其他语言中 静态方法可以访问静态成员 并且它们的可见性受到类范围的限制 在 Fortran 中 没有静态成员 如果我错了 请纠正我 并且方法名称是全局可访问的 因此我什至不能在不同的类中拥有两个同名的静态方法 我认为 nopass
  • CSS 过渡不适用于 max-height: fit-content

    我正在尝试使用 CSS 为一些可扩展面板设置动画 如下所示 panel transition max height 0 1s ease in out flex 90 max height 26px expanded max height f
  • 网格::右对齐网格项目

    通过使用右侧按钮的网格定位 有人能指出我正确的方向吗 container width 500px border 1px solid red grid display grid grid gap 5px grid auto flow colu
  • 在Python中从键盘读取原始输入

    我正在尝试在 python 中获取键盘的原始输入 我有一个带可编程按键的罗技游戏键盘 但罗技不提供适用于 Linux 的驱动程序 所以我想我可以 尝试 为此编写自己的驱动程序 认为解决方案可能是这样的 with open dev keybo
  • 从源代码访问 Visual Studio 宏?

    Visual Studio 有类似的宏 TargetDirectory OutputPath etc 在我的源代码中 我想指定一个相对路径 用于从比该文件低几级的文件夹中加载文件 TargetDirectory 目前我正在这样做 mLaye
  • 如何在命令行中使用 Emacs 启动不同模式?

    有没有办法使用命令行启动不同的 emacs 模式 例如 是否可以按如下方式运行 emacs emacs org mode to start orgmode emacs python mode to start python mode 此后我
  • Hibernate - 为什么使用多对一来表示一对一?

    我见过人们使用多对一映射来表示一对一关系 我还在 Gavin King 的书和文章中读到过这一点 例如 如果一位客户只能有一个送货地址 并且一个送货地址只能属于一位客户 则映射如下
  • App Store - 帮助回答“缺少合规性”(使用 Expo + Firebase)

    我正在将我的应用程序发布到 App Store 但我对 缺少合规性 步骤有疑问 以下是有关该应用程序的一些信息 I used 世博会 托管工作流程 https docs expo io introduction managed vs bar
  • 在php mysql中插入多个值

    我这里有一个用于更新 php mysql 中的多个值的示例代码 我想知道如何插入多个值
  • 企业代理背后的 Apache

    我正在开发一个 php 应用程序 我正在使用 wamp 并且我在公司代理后面 我在用着cntlm http cntlm sourceforge net 进行身份验证NTLM https en wikipedia org wiki NT LA
  • 如何使用 Diesel 和 SQLite 获取新创建值的 id?

    柴油机的SqliteBackend不执行SupportsReturningClause的特质 所以get result方法不能用于检索新创建的值 还有其他方法可以找出插入行的 id 吗 Python 有一个解决方案 https stacko
  • 如何将 lambda 函数读取为字符串?

    我想在创建它之后读取我作为字符串创建的 lambda 函数 例如 func lambda num1 num2 num1 num2 我想将 func 读为 lambda num1 num2 num1 num2 有没有办法实现这一点 甚至读取
  • C++ iomanip 对齐

    我正在尝试调整我的输出 但由于某种原因我无法让它对齐 我多么想要它 这真的很令人沮丧 标题不会正确对齐 我不知道我是否正确使用 setw include
  • HttpContext.Current 调用背后有多少计算量?

    是不是很贵 我正在开发一个直接渲染到 Response Output 的 HtmlHelper 以节省不必要的字符串创建 我需要在以下选项之间进行选择 and 并从 HttpContext Current Response 获取 textW
  • python GIL 的多线程示例

    我读过很多关于在编写多线程代码时 python GIL 业务有多么 糟糕 的文章 但我从未见过一个例子 有人可以给我一个基本的例子来说明 GIL 在使用线程时何时会导致问题吗 Thanks 多线程的主要原因之一是程序可以利用多个 CPU 和