[Python] What operations are thread-safe in Python?

Discussão em 'Python' iniciado por Stack, Maio 12, 2025.

  1. Stack

    Stack Membro Participativo

    I am trying to understand why some particular code snippets behave thread-safe.

    There's a Python's FAQ page which says what mutations are considered thread safe (though I still don't understand why atomicity here implies thread-safety)

    There's also a glossary page which tells a little bit about GIL and states:


    This [GIL] simplifies the CPython implementation by making the object model (including critical built-in types such as dict
    ​

    So my current understanding the following:

    1. GIL ensures that only one VM's instructions (not Python's) is executed in any given moment
    2. Some operations on builtin types decompile into exactly one bytecode instruction which can be considered thread-safe (in a given context)

    But this does not match what I see. The following python function:

    def inc(stats):
    stats["ok"] = stats["ok"] + 1


    is decompiled into (under Python 3.10.14 (main, Mar 19 2024, 21:46:16) [Clang 15.0.0 (clang-1500.3.9.4)] on darwin)

    0 LOAD_FAST 0 (stats)
    2 LOAD_CONST 1 ('ok')
    4 BINARY_SUBSCR
    6 LOAD_CONST 2 (1)
    8 BINARY_ADD
    10 LOAD_FAST 0 (stats)
    12 LOAD_CONST 1 ('ok')
    14 STORE_SUBSCR
    16 LOAD_CONST 0 (None)
    18 RETURN_VALUE


    where we can clearly see that the operation is broken down into three corresponding bytecode instructions (get add set) (BINARY_SUBSCR, BINARY_ADD, STORE_SUBSCR).

    Despite that, running this function multiple times using concurrent.futures.ThreadPoolExecutor always gives consistent results (i.e. no race conditions)

    The same happens for other suspiciously non-thread safe operations like x = x + 1 (which also give consistent results)

    If my understanding is correct we should expect race conditions occasionally.

    I suspect one or more of the following:

    1. My understanding is not correct
    2. There's a special treatment in the CPython for dictionary operations
    3. The operation happens to be fast enough so Python thread scheduler unable to preempt thread in the middle of the executing of the statement (not bytecode)

    UPD: I tried breaking up stats["ok"] = stats["ok"] + 1 into two statements through intermediate variable and inserting short sleeps between those two and observed data races. So 3rd intuition is likely right and stats["ok"] = stats["ok"] + 1 also inherently not thread safe as it also consist of multiple bytecode instructions.

    Continue reading...

Compartilhe esta Página