在多线程Python代码中,是否因为GIL而不需要锁?

2024-04-26 12:57:23 发布

您现在位置:Python中文网/ 问答频道 /正文

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

如果GIL不允许多条指令并行执行,那么共享数据就没有必要进行保护吗?

抱歉,如果这是一个愚蠢的问题,但这是我一直想知道的关于多处理器/核心机器上的Python。

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


Tags: 数据代码机器语言核心指令cpython全局
3条回答

如果在线程之间共享状态,则仍然需要锁。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)之间被中断,从而导致丢失更新。结果是共享状态的随机值。

为了使更新保持一致,run方法需要锁定read-modify-write部分周围的共享状态(在循环内)或具有some way to detect when the shared state had changed since it was read

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

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

添加到讨论中:

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

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

然而,正如其他答案所述,只要应用程序逻辑需要锁(例如在生产者/消费者问题中),您仍然需要使用锁。

相关问题 更多 >