【问题标题】:Are Python built-in containers thread-safe?Python 内置容器是线程安全的吗?
【发布时间】:2010-02-09 06:17:20
【问题描述】:

我想知道 Python 内置容器(list、vector、set...)是否是线程安全的?还是我需要为我的共享变量实现锁定/解锁环境?

【问题讨论】:

    标签: python thread-safety


    【解决方案1】:

    您需要为所有将在 Python 中修改的共享变量实现自己的锁定。您不必担心从不会被修改的变量中读取(即并发读取是可以的),所以不可变类型(frozensettuplestr)可能是 em> 安全,但不会受到伤害。对于您将要更改的内容 - listsetdict 和大多数其他对象,您应该拥有自己的锁定机制(虽然在大多数这些对象上都可以进行就地操作,但线程可以引导对于超级讨厌的错误 - 你不妨实现锁定,这很容易)。

    顺便说一句,我不知道你是否知道,但是在 Python 中锁定非常容易——创建一个 threading.lock 对象,然后你可以像这样获取/释放它:

    import threading
    list1Lock = threading.Lock()
    
    with list1Lock:
        # change or read from the list here
    # continue doing other stuff (the lock is released when you leave the with block)
    

    在 Python 2.5 中,执行 from __future__ import with_statement; Python 2.4 和之前的版本没有这个,所以你需要将 acquire()/release() 调用放在try:...finally: 块中:

    import threading
    list1Lock = threading.Lock()
    
    try:
        list1Lock.acquire()
        # change or read from the list here
    finally:
        list1Lock.release()
    # continue doing other stuff (the lock is released when you leave the with block)
    

    Some very good information about thread synchronization in Python.

    【讨论】:

    • 我相信,对于以前没有使用过线程锁的人来说,应该注意锁(在你的例子中,list1Lock)应该是共享​​>线程,以使其正常工作。两个独立的锁,每个线程一个,不会锁定任何东西,只会增加愚蠢的开销。
    • 不应该是这样的:with list1Lock: #Do stuff
    • 但请考虑一下:“实际上,这意味着对“看起来原子”的内置数据类型(int、list、dict 等)的共享变量的操作确实是。 (effbot source)。那么这样的操作是线程安全的吗?
    • “但它不会伤害”......无论是否必要。
    • Python 文档只在非常偶然的情况下保证线程安全(例如,deque)。因此,如果您想编写适用于所有实现的代码,您通常需要锁。然而,很多人只关心自己的python实现。例如,在 CPython 中,许多集合方法是线程安全的(并且将来可能会保持这种状态,因为很多代码都依赖于它)。锁定可能会带来很大的开销;为避免这种情况,可能需要依赖特定于实现的行为。
    【解决方案2】:

    是的,当然你还是要小心

    例如:

    如果两个线程从只有一个项目的列表中竞争到pop(),一个线程将成功获取该项目,另一个线程将获得IndexError

    这样的代码不是线程安全的

    if L:
        item=L.pop() # L might be empty by the time this line gets executed
    

    你应该这样写

    try:
        item=L.pop()
    except IndexError:
        # No items left
    

    【讨论】:

    • 我希望 pop() 是线程安全的,但我在文档的任何地方都找不到这个事实。在我把它当作福音之前,有人能帮我找到这个说法吗?
    • 真的吗? list.pop() 不是线程安全的?我看到另一篇文章声称相反。 effbot.org/pyfaq/…
    • @Zhongjun'Mark'Jin 他说这是线程安全的.. 但这并不意味着您不必考虑其他线程。如果一个线程弹出最后一项,然后另一个线程也尝试弹出,它会得到 IndexError,正如他所说。
    【解决方案3】:

    只要您不在线程的 C 代码中禁用 GIL,它们就是线程安全的。

    【讨论】:

    • 这是一个 CPython 的实现细节,你不应该继续关注。将来可能会有机会,而其他实现则没有。
    • Georg - python 的这一方面让我感到害怕。不要介意当 8 核在桌面上变得普遍时,Java 程序中会出现的所有错误 - 当 GIL 被删除并且多线程 python 应用程序突然在 8 核机器上运行时会发生什么?
    • 如果他们不假装他们的代码显然不是线程安全的,这不应该吓到任何人。 :)
    • Kylotan - 我认为这是在开玩笑(希望这是正确的!),但以下内容仍然值得一说。您(或任何人)能否直言不讳地说他们从未编写过没有竞争条件的多线程代码?我可能+认为+我有,但我足够现实地接受我在所有情况下都没有做到这一点。 GIL 是一个安全网,可能会在这方面产生错误的信心,灌输不良习惯,当/如果默认 python 容器在 8 台以上 PC 很常见的时候清除 GIL 时,这些不良习惯就会被扫除。这可能是一个有争议的问题,因为有人说 GIL 永远不会消失。时间会证明一切!
    • @PiotrDobrogost:GIL 并不存在于所有 Python 实现中。 python.orgJython 和 IronPython 没有 GIL,可以充分利用多处理器系统。 而且,从长远来看,CPython 试图摆脱 GIL。
    【解决方案4】:

    队列模块实现了多生产者、多消费者队列。当必须在多个线程之间安全地交换信息时,它在线程编程中特别有用。该模块中的 Queue 类实现了所有必需的锁定语义。

    https://docs.python.org/3/library/queue.html

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-11-11
      • 2017-06-19
      • 1970-01-01
      相关资源
      最近更新 更多