当许多线程需要执行时如何操作GIL?

时间:2018-05-19 23:10:52

标签: python c multithreading cpython gil

我的理解是典型的GIL操作涉及例如阻止I / O操作。因此,人们希望在I / O操作之前释放锁,并在完成后重新获取锁。

我目前面临着一个带有C扩展的不同场景:我正在创建通过Canvas类向Python公开的X窗口。在实例上调用方法show()时,将使用PyThreads(调用PyThread_start_new_thread)启动新的UI线程。这个新线程负责使用on_draw子类的Canvas方法中指定的Python代码在X窗口上绘图。在主线程中启动一个纯C事件循环,它只是检查X窗口上的事件,暂时只捕获WM_DELETE_EVENT

所以我可能有很多线程(每个X窗口一个)想要执行Python代码,而主线程根本不执行任何Python代码。

如何释放/获取GIL以便允许UI线程有序地进入解释器?

2 个答案:

答案 0 :(得分:1)

规则很简单:您需要持有GIL才能访问Python机器(任何以Py<...>和任何PyObject开头的API。

因此,只要您不需要,就可以发布它。

除此之外还有fundamental problem of locking granularity:潜在的好处与锁定开销。 There was an experiment for Py 1.4 to replace the GIL with more granular locks that failed exactly because the overhead proved prohibitive.

这就是为什么它通常发布的代码块涉及到可以花费任意时间(特别是如果它们涉及等待外部事件)的扩展设施的呼叫 - 如果你没有&#39;释放锁定,Python将在此期间闲置。

遵循这条规则,您将自动实现目标:每当线程无法继续进行时(无论是I / O,来自另一个线程的信号,还是一个{{1}为了避免繁忙的循环),它将释放锁并允许其他线程继续进行。 GIL分配机制力求fair(参见issue8299,了解它的公平程度),让程序员不必担心任何源于引擎的偏见。

答案 1 :(得分:0)

我认为问题源于这样一个事实:在我看来,official documentation对于非Python创建的线程的含义有点含糊不清。引用它:

  

使用专用Python API (例如threading模块)创建线程时,线程状态会自动与它们关联,因此上面显示的代码是正确的。但是,当从C创建线程时(例如,通过具有自己的线程管理的第三方库),它们不会保留GIL,也不存在线程状态结构。< / p>

我用粗体突出显示了我发现的部分。正如我在OP中所述,我打电话给PyThread_start_new_thread。虽然这会从C创建一个新线程,但此函数不是第三方库的一部分,而是专用Python(C)API的一部分。基于这个假设,我排除了我实际上需要使用PyGILState_Ensure/PyGILState_Release范例。

据我从实验中看到的情况可以看出,使用(只)PyThread_start_new_thread从C创建的线程应被视为非Python创建的线程