Python线程之认识线程安全 

 更新时间:2022年02月24日 10:44:24   作者:雷学委  
这篇文章主要介绍了Python线程之认识线程安全,线程安全,名字就非常直接,在多线程情况下是安全的,多线程操作上的安全,下面学习线程安全的文章详细内容,需要的小伙伴可以参考一下

一、什么是线程安全?

线程安全,名字就非常直接,在多线程情况下是安全的,多线程操作上的安全。

比如一个计算加法的函数,不管是一千个还是一万个线程,我们希望它执行的结果总是正确的,1+1 必须永远等于2, 而不是线程少的时候1+1 变成3或者4了。

通常我们都用线程安全来修饰一个类,修饰一个函数:

我们会说我设计的这个类是线程安全的
这意味着,在多线程环境下,同时调用这个类的函数不会出现函数设置预期之外的异常(上述的1+1=3的情况)

二、在Python中有哪些类是线程安全的?

dict 和 list,tuple这些都是线程安全。

它们是被全局解释器保障了,这个锁:GIL(全局解释器锁)确保了任何时候只能有一个线程执行相应操作的字节码。参考

但是这番话也是说的不清不楚的。

现在我们拿转账来解析吧:

xuewei_account = dict()
xuewei_account['amount'] = 100

# amount为负数即是转出金额
def transfer(money):
    xuewei_account['amount'] +=  money

如上,代码为一个函数对jb_account(账户)进行转入金额操作。

这里用了dict类型,GIL会保证只有一个线程操作账户。

下面是多个线程进行操作的代码:

import random
import threading
import datetime
import time

xuewei_account = dict()
xuewei_account['amount'] = 100


# amount为负数即是转出金额
def transfer(money):
    xuewei_account['amount'] +=  money


# 创建4个任务给重复学委账户转账
threads = []
for i in range(200):
    t1 = threading.Thread(target=lambda: transfer(-1))
    threads.append(t1)
    t2 = threading.Thread(target=lambda: transfer(1))
    threads.append(t2)

for t in threads:
    t.start()
    
# 这次不用sleep了,用join来等待所有线程执行完毕
# join函数必须线程start后才能调用,否则出错。
for t in threads:
    t.join()

print("-" * 16)
print("活跃线程数:", threading.active_count())
print("活跃线程:", threading.current_thread().name)
print("学委账户余额:", xuewei_account)

这段代码运行的输出结果正常,因为是反复+1/-1,最后肯定是恢复原账户余额。

虽然多个线程,但是每个线程只对xuewei_account进行一次读写,这时候dict是安全的。

但是我们把赋值修改dict的操作变多之后(特别是一个线程内反复多次获取值然后修改),像下面的代码:

import random
import threading
import datetime
import time

xuewei_account = dict()
xuewei_account['amount'] = 100


# amount为负数即是转出金额
def transfer(money):
    for i in range(100000):
        xuewei_account['amount'] = xuewei_account['amount'] + money


# 创建400个任务重复给学委账户转账
threads = []
for i in range(200):
    t1 = threading.Thread(target=lambda: transfer(-1))
    threads.append(t1)
    t2 = threading.Thread(target=lambda: transfer(1))
    threads.append(t2)

for t in threads:
    t.start()
for t in threads:
    t.join()

print("-" * 16)
print("活跃线程数:", threading.active_count())
print("活跃线程:", threading.current_thread().name)
print("学委账户余额:", xuewei_account)

这是某一次运行结果(不保证每次acount的数值一样):

我们看到dict还是扛不住多个线程反复的写操作。

这里区别是:每个线程只对xuewei_account进行大量读写,虽然dict是安全的,但是多个线程中间穿插修改了account,程序方法栈出现操作到旧值(看下面的图)。

主要是下面这段代码:

xuewei_account[‘amount'] += money 
# 即是 xuewei_account[‘amount'] = xuewei_account[‘amount']+ money

再一步抽象简化可以写成:

a = a + b

每个线程都执行 +b 操作,最后a的值应该是a+2b。

上面的操作意味这下面的情况发生了:

在某个线程中可能出现某一个线程T1获取了a值 ,准备加上b。

另外一个线程T2已经完成了a+b操作,把a的值变成了a+b了。

但是接下来T1 拿了a的值再执行a+b操作,把a的值变成a+b。

这样就少加了一个b,本来最后结果是a+2b 的变成了 a+b(因为T1拿了a的旧值,中间T2执行完,T1才继续执行)

当然实际多线程之间交互比上图还要随机。

三、如何做到真正线程安全?

dict读取数据是线程安全,但是被反复读写就容易出现数据混乱。

如果我们要设计一个线程安全的函数,那么它必须不涉及任何共享变量或者是完全没有状态依赖的函数

def thread_safe_method():
    pass

1.无状态函数

比如下面的加法函数,不管多少个线程调用,返回值永远是预期的a+b。

def add(a, b):
    return a + b

2.另一种 化繁为简

许我们可以把多线程转换为单线程,这个需要一个线程安全的媒介。

到此这篇关于Python线程之认识线程安全 的文章就介绍到这了,更多相关认识Python线程安全 内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 一文详解loss.item()用法和注意事项

    一文详解loss.item()用法和注意事项

    loss.item()是PyTorch中的一种方法,用于计算损失函数的值,下面这篇文章主要给大家介绍了关于loss.item()用法和注意事项的相关资料,需要的朋友可以参考下
    2023-06-06
  • 详解Python单元测试的两种写法

    详解Python单元测试的两种写法

    python的两个单元测试包分别是 doctest 和 unittest,这两个包的使用起来各有长处,适用于不同的场景,这篇文章主要介绍了Python单元测试的两种写法,需要的朋友可以参考下
    2022-07-07
  • Python动态强类型解释型语言原理解析

    Python动态强类型解释型语言原理解析

    这篇文章主要介绍了Python动态强类型解释型语言原理解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-03-03
  • Python pyside6编写一个广告图片生成器

    Python pyside6编写一个广告图片生成器

    这篇文章主要为大家详细介绍了Python如何使用pyside6编写一个广告图片生成器,可以快速制作包含产品图片和文字的广告图片,需要的可以参考下
    2025-01-01
  • 读取json格式为DataFrame(可转为.csv)的实例讲解

    读取json格式为DataFrame(可转为.csv)的实例讲解

    今天小编就为大家分享一篇读取json格式为DataFrame(可转为.csv)的实例讲解,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-06-06
  • 用python统计代码行的示例(包括空行和注释)

    用python统计代码行的示例(包括空行和注释)

    今天小编就为大家分享一篇用python统计代码行的示例(包括空行和注释),具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-07-07
  • Python实现的查询mysql数据库并通过邮件发送信息功能

    Python实现的查询mysql数据库并通过邮件发送信息功能

    这篇文章主要介绍了Python实现的查询mysql数据库并通过邮件发送信息功能,可实现Python针对mysql的查询与宕机信息通过邮件发送的相关操作技巧,需要的朋友可以参考下
    2018-05-05
  • 女神相册密码忘记了 我只用Python写了20行代码

    女神相册密码忘记了 我只用Python写了20行代码

    这篇文章主要介绍了怎么样用Python写了20行代码来破解找回已经忘记的密码,密码的排列组合是有限的,遍历尝试就能够试出正确的密码,但这个工作要怎么用python实现,请看下文
    2021-08-08
  • 利用python实现.dcm格式图像转为.jpg格式

    利用python实现.dcm格式图像转为.jpg格式

    今天小编就为大家分享一篇利用python实现.dcm格式图像转为.jpg格式,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-01-01
  • 解决python多行注释引发缩进错误的问题

    解决python多行注释引发缩进错误的问题

    今天小编就为大家分享一篇解决python多行注释引发缩进错误的问题,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2019-08-08

最新评论