为何使用multiprocessing.dummy实现的抽样打标函数未对全部col_2取值生效
问题原因分析
- 核心原因是pandas DataFrame 不是线程安全对象,你在多线程中并发修改全局变量
df2时没有加同步锁,多个线程的写操作会出现冲突,导致b、c对应的修改被覆盖或者直接丢失,只有最先完成的a的修改保留了下来。 - 另外你代码中没有显式调用
P.close()和P.join()回收线程池资源,虽然map方法本身是阻塞的,但极端情况下也可能出现主线程提前读取未完成修改的df2的问题。
更优的实现方案
这个需求完全不需要引入多线程,直接用pandas自带的groupby操作就能更简洁的实现,没有并发冲突风险:
import pandas as pd import numpy as np data = np.array([(3, 'a'), (2, 'a'), (1, 'b'), (0, 'c'), (2, 'c'), (3, 'c')], dtype=[('col_1', 'i4'), ('col_2', 'U1')]) df = pd.DataFrame.from_records(data) df['batch'] = 0 def process_group(g): # 每组最多抽2行 sample_num = min(2, len(g)) sampled = g.sample(sample_num) sampled['batch'] = range(1, sample_num + 1) return sampled df2 = df.groupby('col_2', group_keys=False).apply(process_group).reset_index(drop=True)
如果一定要使用多线程实现,需要避免在子线程中直接修改全局DataFrame,改为子线程返回修改结果,主线程统一写入:
import pandas as pd import numpy as np import os from multiprocessing import dummy from random import sample core = os.cpu_count() P = dummy.Pool(processes = core) data = np.array([(3, 'a'), (2, 'a'), (1, 'b'), (0, 'c'), (2, 'c'), (3, 'c')], dtype=[('col_1', 'i4'), ('col_2', 'U1')]) df = pd.DataFrame.from_records(data) df['batch'] = 0 df2 = df.copy() def func(c): idx = df.col_2 == c pop = list(df[idx].index) m = min(2, len(pop)) r = sample(pop, m) return zip(r, range(1, m+1)) results = P.map(func, ['a', 'b', 'c']) # 回收线程池 P.close() P.join() # 主线程统一写入修改 for modify_items in results: for idx, batch_val in modify_items: df2.loc[idx, 'batch'] = batch_val
内容的提问来源于stack exchange,提问作者Akira
相关产品推荐
相关产品推荐

