Python 列表切片线程安全吗?

Is Python list slicing thread safe?

来自 Are lists thread-safe?, I need to know if specifically list slicing is thread safe. It's not clear to me from the linked article What kinds of global value mutation are thread-safe?.

根据对 is list.pop thread safe in python 的回答,看来我真的需要知道列表切片是否是一个 原子操作

我有一个列表 some_list,另一个线程正在不断地 append 访问它。在主线程中,我有时会偷看(没有 poping):

所以,问题是,在 CPython(我使用 Python 3.10)中,列表切片是线程安全的吗?

tl;dr 索引和切片都是线程安全的,但切片由构建切片和应用切片。他们之间线程可以修改我们的列表。

在您发布的 Whosebug 页面中,您可以找到一个 list of atomic operations 可迭代对象,索引和切片都在那里。

让我们看一下用于切片的字节码:

import dis

l = [1, 2, 3]


def f():
    l[1:3]


dis.dis(f)
  7           0 LOAD_GLOBAL              0 (l)
              2 LOAD_CONST               1 (1)
              4 LOAD_CONST               2 (3)
              6 BUILD_SLICE              2
              8 BINARY_SUBSCR
              (...)

BINARY_SUBSCR是访问切片的时刻,确实是单次操作。检查:

import time
import threading

l = [0] * 1000000000


def append():
    start = time.perf_counter()
    time.sleep(0.1)
    l.append(1)  # Doesn't execute until main thread access slice
    print("append", time.perf_counter() - start)  # 3.4184917740058154


t = threading.Thread(target=append)
t.start()
start = time.perf_counter()
x = l[:]  # Reading via slicing
print("main", time.perf_counter() - start)  # 3.418436762993224
print("last_elem", x[-1])  # 0
t.join()

所以当访问一个切片时(和访问一个索引一样)不要担心列表的变化,因为它是简单的操作码并且是受 GIL 保护。

那会发生什么?

# l = [1, 2, 3]
x = l[0:3]

您不能假设 x 将包含所有列表。

  1. 我们首先 (BUILD_SLICE) 从 0 开始,到 3 结束。
  2. 然后另一个线程可以修改列表,例如通过追加元素。
  3. 只有这样我们才能以线程安全方式访问切片。

它会使我们的程序崩溃吗?我怀疑,因为使用越界切片是安全的。