从“ Python 内置的字典是线程安全的吗?”谈起

 在 python 官方文档中有这样一段关于 GIL 的解释:

The mechanism used by the CPython interpreter to assure that only one thread executes Python bytecode at a time. This simplifies the CPython implementation by making the object model (including critical built-in types such as dict) implicitly safe against concurrent access. Locking the entire interpreter makes it easier for the interpreter to be multi-threaded, at the expense of much of the parallelism afforded by multi-processor machines.

简单的说就是在实现的时候 CPython 解释器假定只有一个线程执行 python 的字节码,保证单一操作的原子性,使类似于 dict 这个数据类型隐式的线程安全。

但是我们对字典的操作通常是复合操作(插入、删除、更新),这些步骤并不是原子性的。在多线程抢占解释器锁时(特别是多核CPU的机器中),就可能会导致数据不一致。因此,字典类型在多线程环境中并不是线程安全的,因为它的操作不满足原子性,需要额外的同步机制(如锁)来保证在多线程环境下的正确使用。


那么对于 python 变量的赋值是原子性的吗?(refer)

Simple assignment to simple variables is "atomic" AKA threadsafe (compound assignments such as += or assignments to items or attributes of objects need not be, but your example is a simple assignment to a simple, albeit global, variable, thus safe).

简单的赋值操作是原子性的,但对于组合的赋值操作就可能不是,可以看下面几个例子:

# L & L1 是列表,x 是对象,D & D1 是字典, i & j 是 int
L.append(x)
L1.extend(L2)
x = L[i]
x = L.pop()
L1[i:j] = L2
L.sort()
x = y
x.field = y
D[x] = y
D1.update(D2)
D.keys()

但是下面这些操作就不是原子性的 

i = i+1
L.append(L[-1])
L[i] = L[j]
D[x] = D[x] + 1

怎么理解上面这两段的区别呢?可以使用 python 内置的 dis 模块输出函数执行的字节码,对比下下面两个函数的字节码:

忽略上面的 LOAD_FAST 和 LOAD_CONST, 可以看到第一种赋值操作只涉及一条指令的执行,这条指令执行是原子性的,所以这个赋值操作是线程安全的;而第二种赋值操作涉及两条指令,不满足原子性,那么这个操作就是线程不安全的。


所以扯这么多,其实不依赖于语言本身的实现,利用队列、锁、信号量来实现反而是更推荐的做法。当然也可以实现 ThreadSafeDict 的方式来达到目的,这里提供一段样例代码, 当然类似 get、update、__contain__ 这里并没有实现,读者可以自己补充

from collections.abc import MutableMapping


class ThreadSafeDict(MutableMapping):
    def __init__(self, *args, **kwargs):
        self._dict = dict(*args, **kwargs)
        self._lock = Lock()
        
    def __repr__(self):
        return self._dict.__repr__()
        
    def __getitem__(self, k):
        with self._lock:
            return self._dict[k]
    
    def __setitem__(self, k, v):
        with self._lock:
            self._dict[k] = v
            
    def __delitem__(self, k):
        with self._lock:
            del self._dict[k]

    def __iter__(self):
        with self._lock:
            return self._dict.__iter__()

    def __len__(self):
        with self._lock:
            return self._dict.__len__()

  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值