线程锁-何时真正需要?

时间:2018-08-06 15:32:04

标签: python multithreading thread-safety locking

我一直在大量使用线程(并行处理)和锁(防止对共享对象的同时操作)。由于我编写的代码具有很高的并行线程处理速度,接收数据并填充共享数据缓冲区,所以我想知道何时真正需要锁定?

  • 编写共享对象
  • 读取共享对象
  • 更新共享对象的内容

我知道主要是第三种情况很关键(鉴于著名的线程和锁“增量计数器”示例)。但是在其他情况下我也应该使用锁吗?

在我的特殊情况下,它与用作数据缓冲区的熊猫数据帧有关。我要:

  • 向其中添加新数据
  • 从中获取数据
  • 从中删除数据(创建循环缓冲区)

下面的最小工作示例(MWE)使用线程显示了此过程,但是为了简化起见,顺序进行了处理,并在过程之间进行了严格锁定。尽管这是一种超级谨慎的方法,但我想某些获取/释放锁定步骤可能会被放弃吗?但是由于熊猫在附加对象时会复制对象,因此我不确定100%是否放弃这些锁。

有人对此进行了严格的测试还是有人有经验?


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 :(得分:0)

您选择哪种locking discipline对哪种data consistency对您很重要。执行读取时,您是否需要最新数据。例如,您可以选择在读取数据帧时不包括锁定,而仅在进行一些计算后重新分配它时才锁定。但是,您似乎想要完全的一致性保证,您当前的锁定规则可以很好地使用它。此外,从多个线程进行写入时,pandas DataFrame没有内部一致性保证,因此您必须同时锁定。

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