我正在使用多个线程来访问和删除我的 pandas 数据框中的数据。正因为如此,我想知道熊猫数据帧是线程安全的吗?
2 回答
不,熊猫不是线程安全的。而且它以令人惊讶的方式不是线程安全的。
- 当另一个线程正在使用时,我可以从熊猫数据框中删除吗?
胡思乱想!没有。而且一般不会。甚至对于 GIL 锁定的 python 数据结构也不行。
- 我可以在别人写的时候从 pandas 对象中读取吗?
- 我可以在我的线程中复制 pandas 数据框并进行复制吗?
当然不。有一个长期悬而未决的问题:https ://github.com/pandas-dev/pandas/issues/2728
实际上,我认为这是非常合理(即预期)的行为。我不希望能够同时写入和读取或复制任何数据结构,除非:i)它是为并发设计的,或者 ii)我对该对象和所有视图对象都有一个排他锁派生自它(.loc
,.iloc
是视图,pandas 可能有其他视图)。
- 我可以在没有其他人写入的情况下读取 pandas 对象吗?
对于 Python 中的几乎所有数据结构,答案都是肯定的。对于熊猫,没有。看来,这不是目前的设计目标。
通常,如果没有人执行变异操作,您可以对对象执行“读取”操作。不过,你必须要小心一点。一些数据结构,包括 pandas,执行记忆化,以缓存昂贵的操作,否则这些操作在功能上是纯的。在 Python 中实现无锁记忆通常很容易:
@property
def thing(self):
if _thing is MISSING:
self._thing = self._calc_thing()
return self._thing
...它简单而安全(假设分配是安全的原子的——并非所有语言都如此,但在 CPython 中,除非你重写__setattribute__
)。
Pandas、series 和 dataframe 索引在第一次使用时是惰性计算的。我希望(但我在文档中看不到保证)它们以类似的安全方式完成。
对于所有库(包括熊猫),如果没有人执行变异操作,我希望所有类型的只读操作(或更具体地说,“功能纯”操作)都是线程安全的。我认为这是一个“合理的”容易实现的、常见的、较低的线程安全标准。
但是,对于 pandas,您不能假设这一点。即使您可以保证没有人对您的对象执行“功能不纯”的操作(例如写入单元格、添加/删除列),pandas 也不是线程安全的。
这是一个最近的例子:https ://github.com/pandas-dev/pandas/issues/25870 (它被标记为 .copy-not-threadsafe 问题的副本,但它似乎可能是一个单独的问题)。
s = pd.Series(...)
f(s) # Success!
# Thread 1:
while True: f(s)
# Thread 2:
while True: f(s) # Exception !
... 失败f(s): s.reindex(..., copy=True)
,它返回结果 a 作为新对象——你会认为它在功能上是纯的和线程安全的。不幸的是,事实并非如此。
这样做的结果是,我们无法在生产环境中将 pandas 用于我们的医疗保健分析系统——我现在不鼓励将它用于内部开发,因为它会使只读操作的内存中并行化变得不安全。(!!)
这种reindex
行为很奇怪而且令人惊讶。如果有人对它失败的原因有任何想法,请在此处回答: pandas.Series.reindex(, copy=True) 的这种用法中线程不安全的根源是什么?
维护者将此标记为 https://github.com/pandas-dev/pandas/issues/2728的副本。我很怀疑,但如果.copy
是来源,那么几乎所有的熊猫在任何情况下都不是线程安全的(这是他们的建议)。
!
可以以线程安全的方式访问底层 ndarray 中的数据,并自行承担修改风险。删除数据会很困难,因为更改 DataFrame 的大小通常需要创建一个新对象。我想在未来的某个时候改变这一点。