关于Python全局变量的thread-safe

作者: veaxen 分类: Python 发布时间: 2019-06-20 18:16

我们知道python的全局解释器锁(GIL)用于确保Python同一时刻只能有一条线程在执行(也就是其实Python中的多线程是假的,将其理解成协程似乎更加合理)。在Python多线程运行的情况下,全局解释器一般只在字节码之间进行线程切换,所以,我们在编写Python代码的时候,可以认为一条字节码的执行是“原子的”(字节码对应的C实现也被认为是“原子执行的”)。

理解了上面的实现之后,我们知道,只要知道python代码对应的字节码是什么,我们就可以知道那些操作是“原子的”;但是实际上,我们不可能当“人肉解释器”。这里,我们只想知道我们常见的共享于python多线程的变量的哪些操作是“原子的”。对于buildin 数据类型(int,list,dict等)的简单操作通常是只用一条字节码就完成的,但是复杂的操作则不一定了。

例如对于内建类型,下面的操作是原子的(L、L1、L2是list,D、D1、D2是dict,x、y是object,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

通常,那些引起replace其他对象的操作,可能会触发对象的 __del__ 方法,因此不是原子的。需要特别注意的是对list和dict的批量更新,这通常也不是原子的。

讲了这么多,其实有时很难区分对对象的哪些操作是 simple operation(对应于原子操作),哪些是 composite operations(对应于非原子操作)。所以《Google Python style guide》建议:

Do not rely on the atomicity of built-in types.
While Python’s built-in data types such as dictionaries appear to have atomic operations, there are corner cases where they aren’t atomic (e.g. if hash or eq are implemented as Python methods) and their atomicity should not be relied upon. Neither should you rely on atomic variable assignment (since this in turn depends on dictionaries).

我们不应该依赖CIL带来的原子性,当有thread-safe疑虑的时候,应该毫不犹豫就上锁。


参考 《What kinds of global value mutation are thread-safe?》

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!

发表评论

电子邮件地址不会被公开。 必填项已用*标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据