Python 线程锁-何时真正需要?
我一直在大量使用线程(并行处理)和锁(防止同时操纵共享对象)。由于我以并行线程的极高处理速度编写代码,接收数据并填充共享数据缓冲区,我想知道什么时候真的需要锁Python 线程锁-何时真正需要?,python,multithreading,thread-safety,locking,Python,Multithreading,Thread Safety,Locking,我一直在大量使用线程(并行处理)和锁(防止同时操纵共享对象)。由于我以并行线程的极高处理速度编写代码,接收数据并填充共享数据缓冲区,我想知道什么时候真的需要锁 写入共享对象 读取共享对象 更新内容中给定的共享对象 我知道第三种情况主要很关键(考虑到著名的线程和锁的“增量计数器”示例)。但是我应该在其他情况下也使用锁吗 在我的特殊情况下,它是关于一个用作数据缓冲区的数据帧。我想: 向其中添加新数据 从中获取数据 从中删除数据(创建循环缓冲区) 下面的最小工作示例(MWE)显示了这个带有线程
- 写入共享对象
- 读取共享对象
- 更新内容中给定的共享对象
- 向其中添加新数据
- 从中获取数据
- 从中删除数据(创建循环缓冲区)
MWE:
输出:
**********1**********
钥匙
0 1.0
********** 2 **********
钥匙
0 1.0
1 2.0
**********提取**********
数据提取
钥匙
0 1.0
数据缓冲区
钥匙
1 2.0
********** 3 **********
钥匙
0 2.0
1 3.0
你选择什么对你来说很重要。执行读取时,是否需要最新的数据。例如,您可以选择在读取数据帧时不包含锁,而在经过一些计算后重新分配数据帧时只包含锁。然而,您似乎需要完全的一致性保证,而您当前的锁定规程可以很好地使用。此外,当从多个线程写入数据时,pandas DataFrame没有内部一致性保证,因此在执行此操作时必须锁定
但是,您还必须知道,
cpython
实现使用了一个或全局解释器锁,在任何给定时间只允许执行一个python“线程”。要获得实际的并行性,您必须使用从GIL
中解放出来的并行性。由于这一事实,我怀疑上面的代码执行速度是否比在单个线程中运行此操作快 任何时候一个线程可能正在写入一个共享对象,你都需要一个锁。您唯一不需要锁的时候是并发访问只读对象。但这意味着在我的示例中,我将始终需要锁。Processadd_data
通过添加行肯定会写入它,drop_data
也会通过删除行来操作对象,get_data
需要更新索引,所以也会修改碎片对象?是,可变数据是导致并发处理困难的原因。是的,我知道“线程”存在GIL
/cpython
问题。我处于并行执行不同进程的情况下(在线程中侦听MQTT代理的消息(MQTT
module循环),将接收到的消息的数据添加到各个线程中的缓冲区,主线程在给定的时间间隔内处理数据)。因为它“伪造”了并行处理,所以我对它没意见。到目前为止,它似乎以0(50毫秒)的间隔跟上了传入消息的速度。但是,我希望减少由于锁定而导致的死区时间,因此尝试减少锁获取/释放过程。
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