来自列表线程安全吗?,我需要知道特定列表切片是否是线程安全的。从链接文章中我不清楚什么样的全局值突变是线程安全的?。
根据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将包含所有列表。
- 我们首先(BUILD_SLICE(从0开始,到3结束
- 然后另一个线程可以修改列表,例如通过附加元素
- 只有这样,我们才能使用线程安全方式访问切片
它会让我们的程序崩溃吗?我怀疑,因为使用界外切片是安全的。