You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python Pandas多线程操作DataFrame遇AssertionError: Gaps in blk ref_locs求解

关于AssertionError: Gaps in blk ref_locs的解释与解决方法

错误含义

这个错误是Pandas内部的一致性检查失败导致的,根源在于Pandas的DataFrame/Series不是线程安全的。当多个线程同时对同一个DataFrame进行修改操作时,会破坏其内部维护的数据块(blk)的引用位置(ref_locs)的连续性——Pandas期望这些引用位置是连续无间隙的,但并发修改会让数据块的结构被打乱,触发断言报错。

从你的代码来看,两个线程同时在修改df1的不同行,即使看起来操作的是不同行,Pandas内部的内存管理结构还是会被并发操作干扰,尤其是当涉及到列的部分更新时,内部的数据块拆分/合并逻辑会在多线程下出问题。

解决方法

针对你的场景,这里有几个可行的解决方案:

1. 给临界区代码加线程锁

最直接的方式是用threading.Lock来保护所有修改df1的操作,确保同一时间只有一个线程能执行这些修改:

import pandas as pd
import threading as th

lock = th.Lock()  # 定义全局锁

def foo():
    t = True
    while True:
        with lock:  # 进入临界区前获取锁,自动处理锁的释放
            if t:
                df1.loc[1, ["A", "B", "C"]] = df2.loc[0, ["A", "B", "C"]]
            else:
                df1.loc[0, ["A", "B", "C"]] = df2.loc[1, ["A", "B", "C"]]
        t = not t

df1 = pd.DataFrame([[1, 2, 3, "a"], [4, 5, 6, "b"]], columns = ["A", "B", "C", "D"])
df2 = pd.DataFrame([[7, 8, 9], [10, 11, 12]], columns = ["A", "B", "C"])

th.Thread(target = foo).start()
th.Thread(target = foo).start()

2. 避免多线程共享同一个DataFrame

如果业务允许,可以让每个线程操作独立的DataFrame副本,这样就不会有并发冲突:

import pandas as pd
import threading as th

def foo(df_copy):
    t = True
    while True:
        if t:
            df_copy.loc[1, ["A", "B", "C"]] = df2.loc[0, ["A", "B", "C"]]
        else:
            df_copy.loc[0, ["A", "B", "C"]] = df2.loc[1, ["A", "B", "C"]]
        t = not t
        # 这里可以将处理后的副本结果同步回主进程,或者直接使用副本

df1 = pd.DataFrame([[1, 2, 3, "a"], [4, 5, 6, "b"]], columns = ["A", "B", "C", "D"])
df2 = pd.DataFrame([[7, 8, 9], [10, 11, 12]], columns = ["A", "B", "C"])

# 每个线程传入df1的独立副本
th.Thread(target = foo, args=(df1.copy(),)).start()
th.Thread(target = foo, args=(df1.copy(),)).start()

3. 使用进程而非线程(规避GIL限制)

由于Python的GIL(全局解释器锁),CPU密集型的多线程操作其实并不能真正并行。如果你的数据操作是CPU密集型的,改用multiprocessing模块的进程会更合适,每个进程有独立的内存空间,不会共享DataFrame:

import pandas as pd
from multiprocessing import Process

def foo(df_copy):
    t = True
    while True:
        if t:
            df_copy.loc[1, ["A", "B", "C"]] = df2.loc[0, ["A", "B", "C"]]
        else:
            df_copy.loc[0, ["A", "B", "C"]] = df2.loc[1, ["A", "B", "C"]]
        t = not t
        # 处理后的结果可以通过队列等方式传递回主进程

df1 = pd.DataFrame([[1, 2, 3, "a"], [4, 5, 6, "b"]], columns = ["A", "B", "C", "D"])
df2 = pd.DataFrame([[7, 8, 9], [10, 11, 12]], columns = ["A", "B", "C"])

Process(target=foo, args=(df1.copy(),)).start()
Process(target=foo, args=(df1.copy(),)).start()

额外提示

你提到删除那两行df1.loc赋值后其他操作能正常运行,这是因为那些操作没有触发Pandas内部数据块的修改,或者修改的方式没有破坏内部的引用结构,但只要涉及到并发修改DataFrame的核心数据,就存在触发这类错误的风险,所以务必保证对共享DataFrame的修改是线程安全的。

内容的提问来源于stack exchange,提问作者cspecial

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.06 18:42:51