gpt4 book ai didi

python - 考虑到 GIL,asyncio 怎么可能不是线程安全的?

转载 作者:太空宇宙 更新时间:2023-11-03 10:55:38 26 4
gpt4 key购买 nike

异步 ​​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 对象保留了非线程安全的选项,即使目前 CPython 中的实现恰好是线程安全的?

最佳答案

其实,不,每个线程就是那个,解释器的一个新线程。

它是由 OS 管理的真正线程,而不是仅供 Python 虚拟机中的 Python 代码使用的内部管理线程。

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

想象一个线程在一个 CPU 上,另一个线程在另一个 CPU 上。纯并行线程,用汇编语言编写。两者同时试图更改注册表值。根本不是理想的情况。访问相同内存位置的汇编指令最终会争先恐后地决定何时何地移动什么。这样的 Action 最终的结果很容易导致segmentation fault。好吧,如果我们用 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() 将阻塞,直到第一个线程完成对它的迭代。这是线程安全的。

关于python - 考虑到 GIL,asyncio 怎么可能不是线程安全的?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41459485/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com