Python列表切片线程安全吗



来自列表线程安全吗?,我需要知道特定列表切片是否是线程安全的。从链接文章中我不清楚什么样的全局值突变是线程安全的?。

根据python中list.pop线程安全的答案,我似乎真的需要知道列表切片是否是原子操作

我有一个列表some_list,另一个线程正在持续append。在主线程中,我有时会偷看(没有pop(:

  • 列表的最后一个元素是这样的:some_list[-1](索引(
  • 列表的最后几个元素是这样的:some_list[-3:](切片(

那么,问题是,在CPython(我使用Python 3.10(中,列表切片线程安全吗?

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

在您发布的stackoverflow页面中,您可以找到可迭代项的原子操作列表,其中包括索引和切片。

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

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. 只有这样,我们才能使用线程安全方式访问切片

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