python多线程和asyncio_python – 考虑到GIL,asyncio如何不是线程安全的?

博客探讨了asyncio库中的对象线程安全问题,强调大多数asyncio对象不是线程安全的,应当避免在事件循环之外访问。文章通过示例解释了GIL如何保护Python对象的原子性,但并不能阻止线程间的并发问题。作者提醒开发者在处理多线程和asyncio时要特别小心,确保正确同步,以避免意外的数据冲突。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

asyncio docs读取:

Most asyncio objects are not thread safe. You should only worry if you access objects outside the event loop.

有人可以解释这一点,或举例说明误用asyncio会导致对线程之间共享的对象进行非同步写入吗?我认为GIL意味着一次只有一个线程可以运行解释器,所以在解释器中发生的事件,比如读取和编写Python对象,在线程之间进行了简单的同步.

上面引用的第二句听起来像是一条线索,但我不知道该怎么做.

我猜一个线程总是可以通过发布GIL并决定写入Python对象来造成破坏,但这并不是特定于asyncio所以我不认为这是文档在这里所指的内容.

这可能是asyncio PEP保留某些asyncio对象的选项不是线程安全的问题,即使CPhthon中的实现恰好是线程安全的吗?

解决方法:

实际上,不,每个线程都是这个,解释器的新线程.

它是由OS管理的真实线程,而不是仅用于Python虚拟机中的Python代码的内部管理线程.

需要GIL来防止非常基于操作系统的线程搞乱Python对象.

想象一个CPU上的一个线程和另一个上的另一个线程.纯并行线程,用汇编语言编写.两者同时试图更改注册表值.根本不是理想的情况.访问相同内存位置的汇编指令最终会争先恐后地移动到何处和何时移动.最终,这种动作的结果很容易导致分段错误.好吧,如果我们用C语言编写,C控制那个部分,这样就不会在C代码中发生. GIL对C级别的Python代码也是如此.因此,实现Python对象的代码在更改它们时不会失去其原子性.想象一个线程将一个值插入到另一个线程中刚刚向下移动的列表中,因为该另一个线程从中移除了一些元素.如果没有GIL,这将会崩溃.

GIL对线程内代码的原子性没有任何作用.它仅适用于内部存储器管理.

即使你有像deque()这样的线程安全对象,如果你在它上面一次做多个操作,没有额外的锁定,你可以从中间插入的另一个线程得到结果.哎呀,问题发生了!

假设一个线程从堆栈中获取一个对象,检查它的某些内容,如果条件是正确的则将其删除.

stack = [2,3,4,5,6,7,8]

def thread1 ():

while 1:

v = stack[0]

sleep(0.001)

if v%2==0: del stack[0]

sleep(0.001)

当然,这是愚蠢的,应该使用stack.pop(0)来避免这种情况.但这是一个例子.

让另一个线程每隔0.002秒添加到堆栈中:

def thread2 ():

while 1:

stack.insert(0, stack[-1]+1)

sleep(0.002)

现在,如果你这样做:

thread(thread2,())

sleep(1)

thread(thread1,())

虽然不太可能,但是会有一个时刻,thread2()尝试在thread1()的检索和删除之间准确地堆叠新项目.因此,thread1()将删除新添加的项而不是正在检查的项.结果不符合我们的意愿.因此,GIL不会控制我们在线程中所做的事情,只是线程在更基本的意义上对彼此做了什么.

想象一下,你写了一个服务器来买一些活动的门票.两个用户连接并尝试同时购买相同的票证.如果你不小心,用户可能会坐在另一个上面.

线程安全对象是执行操作的对象,它不允许在第一个操作完成之前执行其他操作.

例如,如果你在一个线程中迭代deque(),并且在其中间另一个线程尝试追加某些东西,append()将阻塞,直到第一个线程完成迭代.这是线程安全的.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值