2

的ASYNCIO docs读:考虑到GIL,asyncio怎么可能不是线程安全的?

大多数ASYNCIO对象不是线程安全的。如果您访问事件循环外部的对象,则只应该担心。

有人能解释这或给的ASYNCIO的滥用是如何导致在线程之间共享的对象不同步写个例子吗?我认为GIL意味着一次只有一个线程可以运行解释器,因此在解释器中发生的事件(如读取和写入Python对象)在线程之间被简单地同步。

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

我想通过释放GIL并决定写入Python对象总是会造成严重的后果,但不是特定于asyncio,所以我不认为这就是这里的文档所指的。

这可能是asyncio PEPs保留某些asyncio对象的选项不是线程安全的问题,尽管目前在CPython中的实现恰好是线程安全的吗?

+2

某些操作需要多个指令进行同步,其中的Python之间可以通过不同的线程来解释。 GIL永远不会*同步Python程序,与asyncio无关。 - 它只是确保Python对象在C级别上是线程安全的,而不是在Python级别上。 –

回答

1

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

它是由OS,而不是内部管理只是Python的虚拟机中的Python代码线程管理一个真正的线程。

需要GIL来防止非常基于OS的线程搞乱Python对象。

想象一个CPU上的一个线程和另一个CPU上的另一个线程。纯平行线程,用汇编写成。两者同时尝试更改注册表值。根本不可取的情况。访问相同内存位置的汇编指令最终会扰乱什么地点以及何时移动。最后,这种行为的结果可能很容易导致分段错误。那么,如果我们用C语言编写,C就控制这个部分,所以这不会发生在C代码中。 GIL对C级别的Python代码也一样。因此,实现Python对象的代码在更改它们时不会失去其原子性。想象一下,一个线程将一个值插入到另一个线程中正在向下移动的列表中,因为另一个线程从中移除了一些元素。没有GIL就会崩溃。

GIL对线程内的代码的原子性没有任何影响。它仅用于内部内存管理。

即使你有线程安全的对象,如双端队列(),如果你是在一次就可以了,无需额外的锁做一个以上的操作,就可以得到在两者之间插入另一个线程导致。和哎呀,问题发生!

咱们说一个线程需要的对象从堆栈,检查一下东西,如果条件是正确删除。

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

会有一个时刻,尽管这不太可能,其中线程2()尝试线程1之间恰好叠加了新的项目()的检索和删除。因此,thread1()将删除一个新添加的项目,而不是正在检查的项目。结果不符合我们的意愿。所以,GIL不会控制我们在我们的线程中做什么,只是线程正在做什么 - 对于更基本的意义上的其他线程。

想象一下,你写了一个服务器来购买一些事件的票。两个用户连接并尝试同时购买同一张票。如果你不小心,用户可能会结束坐在另一个之上。

线程安全的对象是执行操作的对象,它不允许直到第一个完成的另一个动作发生。例如,如果您在一个线程中迭代deque(),并在另一个线程的中间另一个线程尝试追加某些内容,append()将会阻塞,直到第一个线程完成迭代为止。这是线程安全的。

+0

'GIL不控制我们在我们的线程中正在做什么,只是线程正在对其他线程做什么 - 这条线是纯金。 – xyres

相关问题