细心的人可能已经发现了,在《Cython进阶--用Cython封装Callback函数》中,每次获取GIL都要调用一下PyGILState_Ensure,释放时又要调用PyGILState_Release,
为了防止已经获取GIL的线程在执行I/O操作而导致其它运行Python代码的线程暂时获取不到GIL而被迫暂停,更有甚者如果已经获取GIL的线程调用的某一个C函数在等待一个mutex,而另一个负责释放这个锁的线程却在等待获取GIL,根本无法释放这个mutex,那就造成了死锁!为了避免这种情况的发生,就要在调用C函数之前调用PyEval_SaveThread 暂时释放GIL,函数调用完时调用PyEval_RestoreThread重新获取。
这样是不是太麻烦了,实际上,cython的设计者们早就为我们考虑到了这点,方法就是用with gil和with nogil语句块。
这也再一次印证了一句话:只有想不到的,没有做不到的
你在遇到一个问题时,99.99999999%的概率别人已经解决过了,如果恰巧碰上了0.0000000001%的概率,那么你应该感到幸运。
先来看下面的例子:
首先,在引用pthread_join函数时在文件尾加上了 nogil,表明这个函数可以在with nogil语句块中调用,要不然cython在翻译成c代码的时候报错
int pthread_join (pthread_t __th,void**__thread_return) nogil
再就是在JoinThread时改用了如下的调用方法:
with nogil:
ret =pthread_join(<pthread_t>tid,&thread_return)
可见要比原来的方法简单多了
再来看下start函数的声明,
cdef void * start (void *param)except*with gil:
在最后加上了with gil,表明这个函数在调用时会先获取GIL
有兴趣的可以看下生成的C代码,
with gil会生成如下的代码:
#ifdefWITH_THREAD
PyGILState_STATE __pyx_gilstate_save =PyGILState_Ensure();
#endif
...
#ifdefWITH_THREAD
PyGILState_Release(__pyx_gilstate_save);
#endif
而with nogil会生成如下的代码:
#ifdefWITH_THREAD
PyThreadState *_save;
Py_UNBLOCK_THREADS
#endif
...
#ifdef WITH_THREAD
Py_BLOCK_THREADS
#endif