Ale*_*lex 34 python multithreading
访问/更改字典值是否是线程安全的?
我有一个全球性的词典foo
和多线程与IDS id1
,id2
,... idn
.foo
如果已知每个线程只能使用其与id相关的值,那么访问和更改值是否可以不为它分配锁定,比如说线程id1
只能使用foo[id1]
吗?
Dir*_*irk 52
假设CPython:是的,不是.从多个并发读/写请求不会破坏字典的意义上来说,从共享字典中获取/存储值实际上是安全的.这是由于实现维护的全局解释器锁("GIL").那是:
线程A运行:
a = global_dict["foo"]
Run Code Online (Sandbox Code Playgroud)
线程B运行:
global_dict["bar"] = "hello"
Run Code Online (Sandbox Code Playgroud)
线程C运行:
global_dict["baz"] = "world"
Run Code Online (Sandbox Code Playgroud)
即使所有三次访问尝试都在"相同"时间发生,也不会破坏字典.解释器将以某种未定义的方式序列化它们.
但是,以下序列的结果未定义:
线程A:
if "foo" not in global_dict:
global_dict["foo"] = 1
Run Code Online (Sandbox Code Playgroud)
线程B:
global_dict["foo"] = 2
Run Code Online (Sandbox Code Playgroud)
因为线程A中的测试/设置不是原子的("检查时间/使用时间"竞争条件).所以,如果你锁定东西通常是最好的:
from threading import RLock
lock = RLock()
def thread_A():
lock.acquire()
try:
if "foo" not in global_dict:
global_dict["foo"] = 1
finally:
lock.release()
def thread_B():
lock.acquire()
try:
global_dict["foo"] = 2
finally:
lock.release()
Run Code Online (Sandbox Code Playgroud)
Ale*_*lli 25
使每个线程使用独立数据的最佳,最安全,最便携的方法是:
import threading
tloc = threading.local()
Run Code Online (Sandbox Code Playgroud)
现在每个线程都使用一个完全独立的tloc
对象,即使它是一个全局名称.线程可以获取和设置属性tloc
,tloc.__dict__
如果它特别需要字典,则使用等.
线程的线程本地存储在线程结束时消失; 让线程记录它们的最终结果,put
在它们终止之前将它们的结果放入一个公共实例Queue.Queue
(这本质上是线程安全的).类似地,线程要处理的数据的初始值可以是在线程启动时传递的参数,也可以是从线程获取的参数Queue
.
其他一些半生不熟的方法,例如希望看起来像原子的操作确实是原子的,可能适用于给定版本和Python版本中的特定情况,但很容易被升级或端口破坏.当一个合适,干净,安全的架构易于安排,便携,方便和快速时,没有真正的理由冒这些问题.
yot*_*ota 17
因为我需要类似的东西,所以我降落在这里.我在这个简短的片段中总结了你的答案:
#!/usr/bin/env python3
import threading
class ThreadSafeDict(dict) :
def __init__(self, * p_arg, ** n_arg) :
dict.__init__(self, * p_arg, ** n_arg)
self._lock = threading.Lock()
def __enter__(self) :
self._lock.acquire()
return self
def __exit__(self, type, value, traceback) :
self._lock.release()
if __name__ == '__main__' :
u = ThreadSafeDict()
with u as m :
m[1] = 'foo'
print(u)
Run Code Online (Sandbox Code Playgroud)
因此,您可以使用with
构造来保持锁定,同时摆弄你的dict()
该GIL需要的是关心,如果你碰巧使用CPython
。
全局解释器锁
Python线程用来确保一次仅一个线程在CPython虚拟机中执行的锁。确保没有两个进程可以同时访问相同的内存,从而简化了CPython的实现。锁定整个解释器可以使解释器更容易成为多线程的,但会牺牲多处理器机器提供的许多并行性。过去,人们一直在努力创建“自由线程”解释器(该解释器以更精细的粒度锁定共享数据),但是到目前为止,都没有成功,因为在普通的单处理器情况下性能会受到影响。