python列表是线程安全的吗,关于python:列表是否是线程安全的?

我注意到,通常建议使用多线程队列,而不是列表和.pop()。这是因为列表不是线程安全的,还是因为其他原因?

很难说在Python中什么是保证线程安全的,很难解释其中的线程安全性。即使是极受欢迎的比特币钱包Electrum也有可能由此产生并发错误。

列表本身是线程安全的。在cpython中,gil防止并发访问它们,而其他实现则注意为它们的列表实现使用细粒度锁或同步数据类型。但是,尽管列表本身不能通过尝试并发访问而损坏,但列表的数据不受保护。例如:

L[0] += 1

如果另一个线程执行相同的操作,则不能保证实际增加l[0],因为EDOCX1[0]不是原子操作。(非常少的操作实际上是原子操作,因为大多数操作都会导致调用任意的python代码。)您应该使用队列,因为如果只使用未受保护的列表,则可能会因为争用条件而获取或删除错误的项。

德克也安全吗?它似乎更适合我使用。

所有Python对象都具有相同的线程安全性——它们本身不会损坏,但它们的数据可能会损坏。collections.deque是queue.queue对象后面的内容。如果您从两个线程访问内容,那么您真的应该使用queue.queue对象。真的?

莱米安,德克是线程安全。在fluent python的第2章中:"class collections.deque是一个线程安全的双端队列,设计用于从两端快速插入和删除。[…]APPEND和POPLEFT操作是原子操作,因此在多线程应用程序中可以安全地将DEQUE用作后进先出队列,而无需使用锁。"

这个答案是关于cpython还是关于python?Python本身的答案是什么?

@尼尔斯:嗯,你链接到的第一个页面是python而不是cpython,因为它描述的是python语言。第二个链接字面意思是有多个Python语言的实现,恰好有一个更流行。考虑到这个问题是关于Python的,答案应该描述可以保证在任何符合规范的Python实现中发生的事情,而不仅仅是在cpython中发生的事情。

@你想不想讨论这个话题?你说,你不想。看来你改变主意了。好啊。您的问题的答案是:Python的答案与cpython的答案相同,因为这是引用实现。是的,它可能对在其他实现中如何实现感兴趣,但您的问题是这个答案是关于cpython还是关于python?答案是:两者都有。python不是其他实现。只不过是塞顿。

为了澄清托马斯出色的回答中的一点,应该提到append()是线程安全的。

这是因为一旦我们开始写入数据,就不需要担心正在读取的数据将在同一位置。append()操作不读取数据,只将数据写入列表。

pylist_append正在从内存中读取。你的意思是它的读写发生在同一个gil锁中吗?github.com/python/cpython/blob/…

@阿姆温特是的,打给埃多克斯的整个电话都是在一个金库里完成的。它提供了对要附加的对象的引用。该对象的内容可能会在评估后和调用PyList_Append之前更改。但它仍然是同一个对象,并安全地附加(如果您执行lst.append(x); ok = lst[-1] is x,那么ok当然可能是错误的)。您引用的代码不会从附加的对象中读取,除非增加它的值。它读取并可能重新分配附加到的列表。

Dotankohen的观点是,L[0] += x将在L上执行__getitem__,然后在L上执行__setitem__--如果L支持__iadd__它在对象接口上执行的操作略有不同,但在python解释器级别上L上仍有两个独立的操作(您将在中看到它们编译的字节码)。append是在字节码中的单个方法调用中完成的。

这很有帮助。我知道li.append(item)是threadsafe,但我想li += [item]不是threadsafe,对吧?

埃多克斯1〔15〕怎么样?

@Greggo听起来像是当前的cpython实现,而不是python。例如,Pypy的情况也一样吗?

@罗伯特·格兰特,我的评论确实是关于塞顿的。我不知道其他实现。

恭喜!那么,我可以连续地追加到一个线程中并弹出另一个线程吗?

这里有一个全面但非详尽的list操作示例列表,以及它们是否是线程安全的。希望得到关于obj in a_list语言结构的答案。

第一个链接似乎已断开。

我最近遇到了这样的情况,我需要在一个线程中连续地追加到一个列表,循环遍历这些项并检查该项是否已就绪,在我的情况下它是一个AsyncResult,只有当它已就绪时才将其从列表中删除。我找不到任何能清楚地证明我的问题的例子。下面是一个示例,演示如何在一个线程中连续地添加到列表中,并在另一个线程中连续地从同一列表中删除。有缺陷的版本很容易在较小的数字上运行,但要保持足够大的数字并运行几次,您就会看到错误。

有缺陷的版本

import threading

import time

# Change this number as you please, bigger numbers will get the error quickly

count = 1000

l = []

def add():

for i in range(count):

l.append(i)

time.sleep(0.0001)

def remove():

for i in range(count):

l.remove(i)

time.sleep(0.0001)

t1 = threading.Thread(target=add)

t2 = threading.Thread(target=remove)

t1.start()

t2.start()

t1.join()

t2.join()

print(l)

出错时输出

Exception in thread Thread-63:

Traceback (most recent call last):

File"/Users/zup/.pyenv/versions/3.6.8/lib/python3.6/threading.py", line 916, in _bootstrap_inner

self.run()

File"/Users/zup/.pyenv/versions/3.6.8/lib/python3.6/threading.py", line 864, in run

self._target(*self._args, **self._kwargs)

File"", line 13, in remove

l.remove(i)

ValueError: list.remove(x): x not in list

使用锁的版本

import threading

import time

count = 1000

l = []

r = threading.RLock()

def add():

r.acquire()

for i in range(count):

l.append(i)

time.sleep(0.0001)

r.release()

def remove():

r.acquire()

for i in range(count):

l.remove(i)

time.sleep(0.0001)

r.release()

t1 = threading.Thread(target=add)

t2 = threading.Thread(target=remove)

t1.start()

t2.start()

t1.join()

t2.join()

print(l)

产量

[] # Empty list

结论

正如前面的回答中所提到的,当从列表中附加或弹出元素的行为本身是线程安全的时,不线程安全的是当您在一个线程中附加并在另一个线程中弹出时。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值