当前位置:  开发笔记 > 编程语言 > 正文

由于GIL,多线程Python代码中是否需要锁定?

如何解决《由于GIL,多线程Python代码中是否需要锁定?》经验,为你挑选了5个好方法。

如果您依赖于具有全局解释器锁(即CPython)和编写多线程代码的Python实现,那么您真的需要锁吗?

如果GIL不允许并行执行多条指令,那么共享数据是否不需要保护?

对不起,如果这是一个愚蠢的问题,但我总是想知道多处理器/核心机器上的Python.

同样的事情适用于任何其他具有GIL的语言实现.



1> Will Harris..:

如果在线程之间共享状态,则仍需要锁定.GIL仅在内部保护解释器.您仍然可以在自己的代码中使用不一致的更新.

例如:

#!/usr/bin/env python
import threading

shared_balance = 0

class Deposit(threading.Thread):
    def run(self):
        for _ in xrange(1000000):
            global shared_balance
            balance = shared_balance
            balance += 100
            shared_balance = balance

class Withdraw(threading.Thread):
    def run(self):
        for _ in xrange(1000000):
            global shared_balance
            balance = shared_balance
            balance -= 100
            shared_balance = balance

threads = [Deposit(), Withdraw()]

for thread in threads:
    thread.start()

for thread in threads:
    thread.join()

print shared_balance

在这里,您的代码可以在读取共享状态(balance = shared_balance)和将更改的结果写回(shared_balance = balance)之间中断,从而导致更新丢失.结果是共享状态的随机值.

为了使更新保持一致,运行方法需要锁定读 - 修改 - 写部分(在循环内)的共享状态,或者有一些方法来检测共享状态自读取以来的变化.



2> Brian..:

不 - GIL只是保护python内部机制免受多线程改变其状态的影响.这是一个非常低级别的锁定,足以使python自己的结构保持一致状态.它不包括您需要做的应用程序级别锁定,以涵盖您自己的代码中的线程安全性.

锁定的本质是确保特定的代码仅由一个线程执行.GIL强制执行此操作以阻止单个字节码的大小,但通常您希望锁定跨越比此更大的代码块.



3> Bruno Gomes..:

加入讨论:

因为GIL存在,所以某些操作在Python中是原子的,不需要锁定.

http://www.python.org/doc/faq/library/#what-kinds-of-global-value-mutation-are-thread-safe

但是,正如其他答案所述,您需要在应用程序逻辑需要时使用锁(例如在生产者/消费者问题中).



4> rcreswick..:

这篇文章描述了相当高级别的GIL:

https://web.archive.org/web/20080516010343/http://www.pyzine.com/Issue001/Section_Articles/article_ThreadingGlobalInterpreter.html

特别感兴趣的是这些引用:

每十条指令(默认可以更改),核心就会释放当前线程的GIL.此时,操作系统从竞争锁定的所有线程中选择一个线程(可能选择刚刚发布GIL的相同线程 - 您无法控制选择哪个线程); 该线程获取GIL,然后运行另外十个字节码.

请注意,GIL仅限制纯Python代码.可以编写扩展(通常用C编写的外部Python库)来释放锁,然后允许Python解释器与扩展分开运行,直到扩展重新获取锁.

听起来GIL只是为上下文切换提供了更少的可能实例,并且使得多核/处理器系统在每个python解释器实例方面都表现为单个核心,所以是的,您仍然需要使用同步机制.


注意,`sys.getcheckinterval()`告诉你在"GIL版本"之间执行了多少字节码指令(从至少2.5开始它已经是100(不是10)).在3.2中,它可能会切换到基于时间的间隔(5ms左右)而不是指令计数.这一变化也可能适用于2.7,尽管它仍在进行中.

5> David Eyk..:

全局解释器锁可以防止线程同时访问解释器(因此CPython只使用一个核心).但是,据我所知,线程仍然被抢先中断和调度,这意味着您仍然需要锁定共享数据结构,以免线程踩到彼此的脚趾.

我一次又一次遇到的答案是Python中的多线程很少值得开销,因为这样.我听说过有关PyProcessing项目的好消息,它使多个进程像多线程一样"简单"运行,具有共享数据结构,队列等.(PyProcessing将作为多处理模块引入到即将发布的Python 2.6的标准库中.)这可以让你绕过GIL,因为每个进程都有自己的解释器.

推荐阅读
Life一切安好
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有