站长网 安全 盘点Python 里关于线程安全的那些事儿

盘点Python 里关于线程安全的那些事儿

1. 线程不安全是怎样的? 要搞清楚什么是线程安全,就要先了解线程不安全是什么样的。 比如下面这段代码,开启两个线程,对全局变量 number 各自增 10万次,每次增量 1。 fromthreadingimportThread,Lock number=0 deftarget(): globalnumber for_inrange(1

1. 线程不安全是怎样的?

要搞清楚什么是线程安全,就要先了解线程不安全是什么样的。

比如下面这段代码,开启两个线程,对全局变量 number 各自增 10万次,每次增量 1。

from threading import Thread, Lock 

 

number = 0 

 

def target(): 

    global number 

    for _ in range(1000000): 

        number += 1 

 

thread_01 = Thread(targettarget=target) 

thread_02 = Thread(targettarget=target) 

thread_01.start() 

thread_02.start() 

 

thread_01.join() 

thread_02.join() 

 

print(number) 

正常我们的预期输出结果,一个线程自增100万,两个线程就自增 200 万嘛,输出肯定为 2000000 。

可事实却并不是你想的那样,不管你运行多少次,每次输出的结果都会不一样,而这些输出结果都有一个特点是,都小于 200 万。

以下是执行三次的结果

1459782 

1379891 

1432921 

这种现象就是线程不安全,究其根因,其实是我们的操作 number += 1 ,不是原子操作,才会导致的线程不安全。

2. 什么是原子操作?

原子操作(atomic operation),指不会被线程调度机制打断的操作,这种操作一旦开始,就一直运行到结束,中间不会切换到其他线程。

它有点类似数据库中的 事务。

在 Python 的官方文档上,列出了一些常见原子操作

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() 

而下面这些就不是原子操作

ii = i+1 

L.append(L[-1]) 

L[i] = L[j] 

D[x] = D[x] + 1 

像上面的我使用自增操作 number += 1,其实等价于 number = number + 1,可以看到这种可以拆分成多个步骤(先读取相加再赋值),并不属于原子操作。

这样就导致多个线程同时读取时,有可能读取到同一个 number 值,读取两次,却只加了一次,最终导致自增的次数小于预期。

当我们还是无法确定我们的代码是否具有原子性的时候,可以尝试通过 dis 模块里的 dis 函数来查看

盘点Python 里关于线程安全的那些事儿

本文来自网络,不代表站长网立场,转载请注明出处:https://www.zwzz.com.cn/html/fuwuqi/anquan/2021/0627/11795.html

作者: dawei

【声明】:站长网内容转载自互联网,其相关言论仅代表作者个人观点绝非权威,不代表本站立场。如您发现内容存在版权问题,请提交相关链接至邮箱:bqsm@foxmail.com,我们将及时予以处理。
联系我们

联系我们

0577-28828765

在线咨询: QQ交谈

邮箱: xwei067@foxmail.com

工作时间:周一至周五,9:00-17:30,节假日休息

返回顶部