在python程序中调用cpp的库创建的线程是否受制于GIL?

因为python的多线程,其实不是真的多线程,它会通过GIL来控制线程,导致不管有多少个核,其实在同一时间只有一个线程在跑。错的。Python 的多线程是真的多线程,只不过在任意时刻,它们中只有一个线程能够取得 GIL 从而被允许执行 Python 代码。其它线程要么等着,要么干别的和 Python 无关的事情(比如等待系统 I/O,或者算点什么东西)。
那如果是通过CPP扩展创建出来的线程,可以摆脱这个限制么?很简单,不访问 Python 的数据和方法,就和 GIL 没任何关系。如果需要访问 Python,还是需要先取得 GIL。
我不明白很简单的道理,为什么你们就是不肯理解一下,非要各种传谣造谣呢?GIL 是为了保护 Python 数据不被并发访问破坏,所以当你不访问 Python 的数据的时候自然就可以释放(或者不取得)GIL。反过来,如果需要访问 Python 的数据,就一定要取得 GIL 再访问。
PyObject 等不是线程安全的。多线程访问任何非线程安全的数据都需要先取得对应的锁。Python 所有的 PyObject 什么的都共享一个锁,它就叫 GIL。

■网友
查了一下GIL:In CPython, the global interpreter lock, or GIL, is a mutex that prevents multiple native threads from executing Python bytecodes at once. 你自己的模块开多个线程,如果这些线程不反回去操纵Python interpreter,应当没有关系吧?
■网友
通过cython调用cpp写的扩展是可以实现多线程的。但是需要解锁GIL且多线程部分不能使用任何python的东西。举个例子:cdef extern from "test_cpp.cpp": cdef extern int test_cpp(int n) nogilcdef int test_cy(int n) nogil: cdef int result = 0 result = test_cpp(n)def test(n): return test_cy(n)其中test(n)是标准python函数,然后这个函数调用test_cy(n),而test_cy(n)又调用test_cpp(n)。在nogil关键词的帮助下test_cpp(n)里的代码是可以多线程,不受GIL限制的。不过注意nogil修饰的部分是不允许出现任何python内容,必须都用cdef定义。
■网友
只要你不去操作PyObject就应该不需要

■网友
答案是不受制于GIL, 但是难点在于将c++线程的结果通知回python管控的线程而不受制于GIL,但也有很多解决方法。
比如,c++创建线程(如果是单纯的消费者线程,则没有这个烦恼了), 该线程处理完任务后,将结果放到一个队列中(无锁队列更佳), python线程可以每隔一段时间从这个队列取结果,这样完全不受GIL影响,可以充分利用多核。

■网友
全局“解释器”锁
用解释器么?不用?那你为啥觉得会用到解释器锁。

然而python这个东西,GIL都上了,并发有可能遇到的问题还是会遇到,你这个锁是锁给谁用的啊……

看看隔壁javascript,event loop性能不比你GIL差,而且一个函数执行过程中永远不用担心被抢占,你python还是不行……

而且人家还有worker。

■网友
搞清楚GIL是什么。Global Interpreter Lock,不涉及解释器当然不受控制了。实际上python连你起了线程都不知道。

■网友
你可以用标准库的multiprocessing一定程度上逃避这个问题
■网友
用pthread create_new的话肯定受限的,写扩展的方式不行。比较简单的方法我觉得是通过IO调用c++的程序。比如subprocess。或者通过socket。这两种其实都是比较简单的。比较麻烦的方法就是把c++编译成c lib,c语言的lib,a或者so,静态库a文件不知道能不能用,但是python直接调用c语言的so或者dll(windows上)动态链接库是没问题的,但是程序写起来很麻烦,不如直接走IO容易。
调用c的lib能不能突破GIL不好说(估计不行),但是通过IO肯定没问题。因为完全是两个进程在通讯。c语言开一个基于文件IO的socket十分容易。python写一个基于文件IO的socket更容易几分钟的事情,所以我觉得这是一个很好的方法,而且也不占用系统端口。


推荐阅读