ske*_*rit 13 python variables multithreading dictionary locking
说到线程,我知道你必须确保你没有在另一个线程编辑它的同时编辑变量,因为你的更改可能会丢失(例如,当递增计数器时)
这同样适用于词典吗?或者是字典是变量的集合?
如果每个线程都要锁定字典,那么它会大大降低程序的速度,而每个线程只需要对其自己的小字典进行写访问.
如果不可能,python中是否有某种变量变量,就像在php中一样?
mou*_*uad 23
这同样适用于词典吗?或者是字典是变量的集合?
让我们更一般:
"原子操作"是什么意思?
来自维基百科:
在并发编程中,如果系统的其余部分在瞬间发生,则操作(或操作集)是原子的,可线性化的,不可分割的或不可中断的.原子性是与并发进程隔离的保证.
现在这在Python中意味着什么?
这意味着每个字节码指令都是原子的(至少对于Python <3.2,在新的GIL之前).
这是为什么???
因为Python(CPython)使用全局解释器锁(GIL).CPython解释器使用锁来确保一次只有一个线程在解释器中运行,并使用"检查间隔"(请参阅sys.getcheckinterval())知道在线程之间切换时要执行多少字节码指令(默认设置为100) .
所以现在这意味着什么?
这意味着只能由一个字节码指令表示的操作是原子的.例如,递增变量不是原子的,因为操作是在三个字节码指令中完成的:
>>> import dis
>>> def f(a):
a += 1
>>> dis.dis(f)
2 0 LOAD_FAST 0 (a)
3 LOAD_CONST 1 (1) <<<<<<<<<<<< Operation 1 Load
6 INPLACE_ADD <<<<<<<<<<<< Operation 2 iadd
7 STORE_FAST 0 (a) <<<<<<<<<<<< Operation 3 store
10 LOAD_CONST 0 (None)
13 RETURN_VALUE
Run Code Online (Sandbox Code Playgroud)
那么字典怎么样?
有些操作是原子的; 例如,此操作是原子的:
d[x] = y
d.update(d2)
d.keys()
Run Code Online (Sandbox Code Playgroud)
你自己看:
>>> def f(d):
x = 1
y = 1
d[x] = y
>>> dis.dis(f)
2 0 LOAD_CONST 1 (1)
3 STORE_FAST 1 (x)
3 6 LOAD_CONST 1 (1)
9 STORE_FAST 2 (y)
4 12 LOAD_FAST 2 (y)
15 LOAD_FAST 0 (d)
18 LOAD_FAST 1 (x)
21 STORE_SUBSCR <<<<<<<<<<< One operation
22 LOAD_CONST 0 (None)
25 RETURN_VALUE
Run Code Online (Sandbox Code Playgroud)
请参阅此内容以了解STORE_SUBSCR的作用.
但正如你所看到的,这并非完全正确,因为这个操作:
...
4 12 LOAD_FAST 2 (y)
15 LOAD_FAST 0 (d)
18 LOAD_FAST 1 (x)
...
Run Code Online (Sandbox Code Playgroud)
可以使整个操作不是原子的.为什么?假设变量x也可以被另一个线程更改......或者你想要另一个线程来清除你的字典...我们可以在出现错误的情况下列出许多情况,因此它很复杂!所以我们将在这里应用墨菲定律:"任何可能出错的东西,都会出错".
所以现在怎么办?
如果您仍想在线程之间共享变量,请使用锁:
import threading
mylock = threading.RLock()
def atomic_operation():
with mylock:
print "operation are now atomic"
Run Code Online (Sandbox Code Playgroud)
小智 7
我认为你误解了整个线程的安全问题.它不是变量(或变量变量 - 无论如何都是非常糟糕的,并且就像在其他情况下一样毫无意义 - 更不用说有害了 - 但是关于 - 例如,线程有很多讨厌的方法可以去错误; 它们都来自于在重叠时间从多个线程访问可变的东西 - 这:
它也适用于字典和变量变量(这只是一个可怕的,可怕的语言级别的字符串实现,仅使用字符串键).唯一的解决方案是不使用共享状态开始(函数式语言通过阻止甚至完全不允许可变性来实现这一点,并且它适用于它们)或者为共享的所有内容添加某种锁定(很难做到,但是如果你得到的话)它是对的,至少它正常工作).如果没有两个线程分享该词典中的任何内容,那么你很好 - 但是你应该分开一切,(更多一点)确保他们真的不分享任何东西.