python常见误区“value+=1”并非线程安全
当我们编写多线程程序时,经常需要处理复杂的共享变量和竞态等问题。
“线程安全”,通常被用来形容 某个行为或者某类数据结构,可以在多线程环境下被共享使用并产生预期内的结果。一个典型的满足“线程安全”的模块就是 queue 队列模块。
而我们常做的 value += 1 操作,很容易被想当然的认为是“线程安全”的。因为它看上去就是一个原子操作 (指一个最小的操作单位,执行途中不会插入任何其他操作)。然而真相并非如此,虽然从 Python 代码上来看,value += 1 这个操作像是原子的。但它最终被 Python 解释器执行的时候,早就不再 “原子” 了。
我们可以用前面提到的 dis 模块来验证一下:
1 def incr(value):
2 value += 1
3
4
5 # 使用 dis 模块查看字节码
6 import dis
7
8 dis.dis(incr)
9 0 LOAD_FAST 0 (value)
10 2 LOAD_CONST 1 (1)
11 4 INPLACE_ADD
12 6 STORE_FAST 0 (value)
13 8 LOAD_CONST 0 (None)
14 10 RETURN_VALUE
在上面输出结果中,可以看到这个简单的累加语句,会被编译成包括取值和保存在内的好几个不同步骤,而在多线程环境下,任意一个其他线程都有可能在其中某个步骤切入进来,阻碍你获得正确的结果。
因此,请不要凭借自己的直觉来判断某个行为是否“线程安全”,不然等程序在高并发环境下出现奇怪的 bug 时,你将为自己的直觉付出惨痛的代价。
2. 字符串拼接并不慢
我刚接触 Python 不久时,在某个网站看到这样一个说法: “Python 里的字符串是不可变的,所以每一次对字符串进行拼接都会生成一个新对象,导致新的内存分配,效率非常低”。 我对此深信不疑。
所以,一直以来,我尽量都在避免使用 += 的方式去拼接字符串,而是用 “”.join(str_list) 之类的方式来替代。
但是,在某个偶然的机会下,我对 Python 的字符串拼接做了一次简单的性能测试后发现: Python 的字符串拼接根本就不慢! 在查阅了一些资料后,最终发现了真相。
Python 的字符串拼接在 2.2 以及之前的版本确实很慢,和我最早看到的说法行为一致。但是因为这个操作太常用了,所以之后的版本里专门针对它做了性能优化。大大提升了执行效率。
如今使用 += 的方式来拼接字符串,效率已经非常接近 “”.join(str_list) 了。所以,该拼接时就拼接吧,不必担心任何性能问题。

浙公网安备 33010602011771号