企业网站建设方案如何写,oa办公系统有哪些,高端品牌手机有哪些,个人网站做捐赠发布违法吗GIL 作为 Python 开发者心中永远的痛#xff0c;在最近即将到来的更新中#xff0c;终于要彻底解决了#xff0c;整个 Python 社群都沸腾了
什么是GIL#xff1f;
GIL是英文学名global interpreter lock的缩写#xff0c;中文翻译成全局解释器锁。GIL需要解决的是线程竞…GIL 作为 Python 开发者心中永远的痛在最近即将到来的更新中终于要彻底解决了整个 Python 社群都沸腾了
什么是GIL
GIL是英文学名global interpreter lock的缩写中文翻译成全局解释器锁。GIL需要解决的是线程竞争thread racing的问题所以需要理解GIL的作用机制我们需要了解一些简单的背景知识包括程序在计算机上运行的机制线程和进程的区别还有并行和并发的概念。
程序在计算机上是如何运行的 我们的程序代码是存储在硬盘上当启动程序时cpu会为应用程序启动进程process小应用程序只需要一条进程但大的应用程序如Google Chrome在后台会启动多条进程对应不同的服务。
每条进程都是独立的运行单元计算机会为每条进程分配内存空间并提供必要的计算资源。我们的程序代码在启动进程时会被加载到这个特定的内存区块里作为数据资源供计算机cpu调用。
为了更好地理解这里我们可以把进程想象成高速路上的某条车道那线程thread自然就是车道上跑的汽车。一个程序可以以单线程的方式运行就像只有一辆车在车道上跑当然也可以以多线程的方式运行这就像很多车在同一车道上跑。
进程之间是相对独立运行的而在同一进程上运行的线程之间是共享内存的。这是进程和线程最大的区别这个区别意味着开启多个进程会消耗更多的内存但开启多个线程不会额外增加内存负担。
另外如果一条进程挂了其它进程继续照常运行但如果一条线程挂了相应的进程也会挂掉运行在该进程上的所有线程自然也会跟着挂掉。
这就好比在高速路上如果车道1进程的比照出现交通事故那整条车道都将不能使用但这并不影响其它车道其它进程继续运行的正常运行。同样的道理如果在车道1上有一辆车线程的比照发生故障停在车道上不能动了线程挂了那其它车辆也不能行驶了这条车道暂停服务进入交通管制状态进程挂了直到故障车被拉出车道管制消除车道继续畅通运行进程重启。
并行和并发的区别
理解了进程和线程并行和并发就很好理解了。
并行和并发是计算机处理任务的两种不同模式。
在并行模式parallelism下多任务可以同时运行提高了计算机的性能。这里要注意一点要真正实现并行程序必须在多核机器上运行并行任务同时运行在不同的cpu上在时间片轮上是可以重叠的如下图中Task 1和Task 3。但我们来看Task 1和Task 2是跑在同一个cpu上的而且它们是交替进行这种模式就是并发模式。还有一点很重要这个问题曾经让我困惑很久。那就是并行模式理论上可以通过多线程并行也可以通过多进程并行。
多进程并行就是在每个cpu上启动新的进程每个进程都需要分配独立内存空间而多线程并行是在每个cpu中启动新的线程该线程共享主线程的内存空间。由于Python GIL的限制在Python环境中并行只能通过多进程的模式而其它语言如JavaC等可以通过多线程的方式进行并行运算。 并发模式concurrency基于线程机制计算机将多个任务分配给在同一进程中的不同线程进行运行不过在并发模式下多线程不能同时处理任务但也不是像单线程一样一个接着一个处理而是通过交替循环。如上图所示假设有两个任务Task 1和Task 2分别运行在线程A和B上在并发模式下线程A运行一小段时间后cpu把它挂起并切换到线程B线程B又运行一小段时间后被挂起同时切换回线程A如此循环直到任务完成。
在实践中并行和并发不是非此即彼根据任务的不同程序的运行可以被合理配置最大程度地利用计算机上的cpu资源。还是用上面的图来举例我们将任务分成两个平行的任务单元并将这两个任务单元分配给两个cpu进行并行运算同时在每个cpu里任务单位再细分为两个更小的任务Task 1和Task 2以及Task 3 和Task 4这两个小任务将在各个cpu里两个线程中并发运行。
除了这种任务配置模式还可以有其它的配置方案比如可以让每个任务单元在某些cpu上进行单线程运行在某些cpu上开多线程并发运行这取决于可支配的cpu资源、需要完成的任务的类型以及任务之间的耦合性需要具体问题具体分析。
那并行和并发运行模式分别适合于什么样的场景呢
首先计算机所处理的任务大致可以分为计算密集型和IO密集型任务。
计算密集型顾名思义需要大量的cpu算力比如人工智能模型里大型矩阵运算图形处理等而IO密集型任务是指磁盘IO、网络IO占主要的任务计算量很小不需要消耗太多计算资源大部分在等待的状态比如向服务器请求数据线程要等待服务器的响应。
如果我们的任务都是计算密集型的通过多线程并发模式运行程序并不能带来性能的提高可能反而比单线程下所花的时间更长因为首先并发不是同时处理任务而是短时轮流循环执行子任务并且在线程切换时需要额外消耗计算机资源这样情况适合多任务并行运算。
如果计算机需要处理的是IO密集型任务这时如果用并行模式我们会浪费很多cpu资源因为处理IO密集型任务不需要太多计算资源大部分时间是在等待所以这种情况适合用单cpu并发模式好处是只用一个cpu资源通过交替执行任务可以在IO任务中的等待时间区间里切换线程去执行其它IO任务从而更充分地利用了cpu资源。
还有一种情况如果我们需要处理的任务既有计算密集型任务又有IO密集型任务那我们可以考虑同时用并行和并发在分配任务单元时将计算密集型和IO密集型任务进行混合如此单个cpu上既有有计算密集型又有IO密集型任务那采用多线程并发就会极大地提高计算机运行效率原因是计算机在处理IO密集任务时不需要浪费等待的时间在IO阻塞的情况切换线程到计算密集任务这就用IO等待时间来处理其它计算密集型任务从而提高程序运行效率。
Python语言中的GIL
Python的解释器是CPythonCPython本身并不确保线程安全thread safe也就是解释器不会对多个线程对同一个python对象的操作行为进行约束。这里我们来举个例子。在这个例子中我们考虑两个线程1和2共享同一个变量aa的初始值为1。我们可以允许两个线程以任何顺序对变量a进行两种写操作其中在线程1中我们修改a的值 a a2 在线程2中我们也修改a的值 a a*2 。取决于两个写操作的顺序我们可以有以下几种不同情况。 情况一 a a2 — a a*2 a的最终值为6 情况二 a a * 2 — a a2 这种情况下的a的最终值为4 情况三线程1和线程2同时获取变量a那结果就取决于哪条线程最后修改a的值如果线程1后修改a的值那a的最终值就是3如果线程2后修改a的值那a的最终值是2。 这三种情况都有可能发生所以每次运行程序前我们都无法预知a的最终值这是由于线程竞争带来的side effect虽然程序员在正常情况下都不会去做线程不安全的操作即两个线程可以同时以任何顺序修改一个共有变量但如果对多线程的任务执行顺序不加限制这种错误在理论上是可能发生的特别是当业务代码量很大又有多个程序员同时维护开发代码时犯这种错误的可能性变大。
要避免这种线程不安全的编程行为要么对程序员的编程行为进行约束比如Java中有多线程并行编程的一系列安全规范要么在编程语言层面上设计一种机制杜绝这种不安全的线程行为Python中的GIT就是提供了这种机制。
GIT的概念很简单GIT是个全局变量被所有线程共享。并且GIT在特定的时刻只允许被一条线程获取获取GIT锁的线程就拥有执行任务的权限而其它线程必须先获取GIT锁才可以执行线程任务。
这样一来Python就断了多线程并行的路子。在Python里多线程只能并发。如果需要并行只能通过多进程的模式具体实现通过内置库multiprocessing。 删除 GIL
现在Python 团队已经正式接受了删除 GIL 的这个提议并将其设置为可选模式可谓是利好广大开发者。
做出这一贡献的是一位来自 Meta 的名叫 Sam Gross 的软件工程师他花费了四年多的时间才完成这一工程。
在得知这一消息后大家纷纷叫好深度学习三巨头之一的 Yann LeCun 发文祝贺没有了 GIL现在Python 代码可以自由的执行多线程了。 CPython 核心开发者 Thomas Wouters 撰文描述了 Python 中的无 GIL 细节并对未来发展做了展望。
原文翻译如下
非常感谢所有人对无 GIL 提议的反馈整体上都持积极的支持态度。指导委员会打算接受无 GIL 提议并就以下具体细节与大家分享。
我们的基本设想是
长期来看大约 5 年以上no-GIL 构建应是唯一的构建我们希望非常谨慎地对待向后兼容。我们不希望出现另一个 Python 3 的情况所有适应 no-GIL 构建所需的第三方代码更改应只适用于 with-GIL 构建尽管仍要解决更老 Python 版本的向后兼容性问题。这不适用于 Python 4。我们仍在考虑对这两个构建的 ABI 兼容性和其他细节的要求以及对向后兼容性的影响在我们承诺完全转向 no-GIL 之前希望看到社区的支持。我们不能只是更改默认设置更希望社区弄清自己做什么工作来给予我们支持。我们核心开发团队需要获得新构建模式及相关所有内容的经验。我们要整理现有代码中的线程安全性需要弄明白新的 C API 和 Python API。我们在获得这些洞见时还需要传达给 Python 社区的其他人并确保自身想要做出的更改以及希望其他人做出的更改是可取的在我们默认 no-GIL 设置之前的任何时候如果事实证明了它的破坏性太大导致收益太少我们希望能够改变主意。这也就意味着我们会回滚所有工作因此在我们确定要将 no-GIL 设为默认方式之前特定于 no-GIL 的代码在某种程度上应是可识别的。
目前我们认为未来的道路分为以下三个阶段
短期内我们会将 no-GIL 构建作为一种实验性构建模式大概会在 3.13 版本也有可能推迟到 3.14 版本可用。之所以是实验性的是因为我们核心开发团队虽然支持这一构建模式但不期望整个社区都会支持它。我们需要时间理清自己要做什么至少在 API 设计以及打包和分发方面从而得到社区的支持。我们也不鼓励 distributor 将实验性 no-GIL 构建作为默认解释器发布。中期来看在我们确信得到足够的社区支持并使 no-GIL 的生产使用可行后我们将支持 no-GIL 构建但不是默认方式而是在某个目标日期或某个 Python 版本中使它成为默认方式。具体的时间将取决于很多因素比如 API 更改最终的兼容性如何、社区认为他们仍然需要做多少工作等。我们预计这至少需要一至两年的时间。一旦我们宣布支持预计将有一些 distributor 会开始默认发布 no-GIL。长期来看我们希望 no-GIL 成为默认方式并删除 GIL 的所有痕迹但不会不必要地破坏向后兼容性。我们不希望等太长时间毕竟两种常用的构建模式同时存在会给社区造成很大的负担比如需要双倍测试资源和 debug 场景。但是我们也不能急于求成。我们认为这一过程将需要花费五年的时间。
当然在整个过程中我们整个开发团队将需要实时评估进程并对时间线进行调整。
评论区的小伙伴们你们对 GIL 成为可选是什么看法呢