首页 文章

线程锁 - 真的需要吗?

提问于
浏览
2

我一直在密集使用线程(并行处理)和锁(防止共享对象的同时操作) . 由于我编写的代码具有非常高的并行线程处理速度,接收数据和填充共享数据缓冲区,我想知道什么时候需要锁?

  • 编写共享对象

  • 读取共享对象

  • 更新在其内容上给出的共享对象

我理解主要是第三种情况是关键的(考虑到线程和锁的着名“增量计数器”示例) . 但是我应该在其他情况下使用锁吗?

在我的特殊情况下,它是关于用作数据缓冲区的pandas数据帧 . 我想要:

  • 向其添加新数据

  • 从中获取数据

  • 从中删除数据(创建循环缓冲区)

下面的最小工作示例(MWE)显示了带有线程的这个过程,但为了简单起见,顺序处理,以及进程之间的密集锁定 . 虽然这是一种超级谨慎的方法,但我猜一些获取/释放锁定步骤可能会被删除?但是,由于大熊猫在追加它们时会复制对象,我并不是百分之百地确定丢弃这些锁 .

是否有人对此进行了密集测试,或者有人有任何经验吗?


MWE:

import pandas as pd
import threading

thread_lock = threading.Lock()

df_data_buffer = pd.DataFrame({"key" : []})

def add_data_to_buffer(df_data_ingestion):        
    global df_data_buffer
    thread_lock.acquire()
    df_data_buffer = df_data_buffer.append(df_data_ingestion)
    thread_lock.release()

def get_data_from_buffer(key):
    thread_lock.acquire()
    df_data_buffer.reset_index(inplace=True, drop=True) #required for proper dropping by index
    df_extracted = df_data_buffer.loc[df_data_buffer["key"] == key].copy()
    thread_lock.release()
    drop_data(df_extracted.index)
    return df_extracted

def drop_data_from_buffer(df_index):
    global df_data_buffer
    thread_lock.acquire()
    df_data_buffer.drop(df_index, inplace=True)
    thread_lock.release()
    return True


df_data1 = pd.DataFrame({"key" : [1]})
t_add_data1 = threading.Thread(target=add_data, args=[df_data1])
t_add_data1.start()
t_add_data1.join()
print "*"*10, 1, "*"*10
print df_data_buffer

df_data2 = pd.DataFrame({"key" : [2]})
t_add_data2 = threading.Thread(target=add_data, args=[df_data2])
t_add_data2.start()
t_add_data2.join()
print "*"*10, 2, "*"*10
print df_data_buffer

key=1
df_data_extracted = get_data(key)
print "*"*10, "extract", "*"*10
print "df_data_extracted\n", df_data_extracted
print "df_data_buffer\n", df_data_buffer

print "*"*10, 3, "*"*10
df_data3 = pd.DataFrame({"key" : [3]})
t_add_data3 = threading.Thread(target=add_data, args=[df_data3])
t_add_data3.start()
t_add_data3.join()
print df_data_buffer

输出:

********** 1 **********
   key
0  1.0
********** 2 **********
   key
0  1.0
1  2.0
********** extract **********
df_data_extracted
   key
0  1.0
df_data_buffer
   key
1  2.0
********** 3 **********
   key
0  2.0
1  3.0

1 回答

  • 0

    您选择什么locking discipline对于什么样的data consistency很重要 . 执行读取时,是否需要最新数据 . 例如,您可以选择在读取数据帧时不包含锁定,但仅在进行某些计算后重新分配时锁定 . 但是,您似乎需要完全一致性保证,这是您当前锁定规则适用的 . 此外,当从多个线程写入时,pandas DataFrame没有内部一致性保证,因此您必须在执行此操作时锁定 .

    但是,您还必须知道 cpython 实现使用GIL或全局解释器锁,只允许在任何给定时间执行一个python 'thread' . 要获得实际的并行性,您必须使用multiple processes,它们从 GIL 中解放出来 . 我怀疑上面的代码,由于这个事实,执行速度比在单个线程中运行此操作要快 .

相关问题