在扩展模块中对python的GIL进行解锁

在多线程环境下,python虚拟机按照以下方式运行:
   1. 设置GIL(全局解释器锁)
   2. 切换到一个线程中去
   3. 运行
        a. 指定数量的字节码的指令,或者
        b. 线程主动让出控制(可以条用time.sleep(0) )
   4. 把线程设置为睡眠模式
   5. 解锁GIL
   6. 再次重复以上步骤
调用C/C++扩展函数的时候,GIL会被锁定,直到这个函数结束。由于这期间没有python的字节码被运行,所以线程不会切换

 

如果我在扩展模块中调用阻塞式地I/O函数,那么就会发生比较糟糕的事情。

比如说,我在扩展模块中接受网络数据,如果对端机器没有发送数据,那么我的整个Python程序就会阻塞到这一点,而且不会进行线程的切换。

如果发生这样的事情,python中的多线程机制就形同虚设了。 

 不过,python允许扩展模块自己对GIL进行解锁,方法如下: 

 解锁GIL的代码结构

Save the thread state in a local variable. 
Release the global interpreter lock. 
... Do some blocking I/O operation ... 
Reacquire the global interpreter lock. 
Restore the thread state from the local variable. 

 由于这个步骤十分通用,Python提供了宏以简化代码,如下:

Py_BEGIN_ALLOW_THREADS
... Do some blocking I/O operation ...
Py_END_ALLOW_THREADS

例如,如果我们在代码中使用了 getch 这个函数,那么我们的代码可以这么写:

# getch test
Py_BEGIN_ALLOW_THREADS
int key = getch();
Py_END_ALLOW_THREADS

这段代码会被扩展为如下:

 # getch test

PyThreadState *_save;

_save = PyEval_SaveThread();
int key = getch();
PyEval_RestoreThread(_save);
 

 

posted on 2012-10-28 18:04  sanlo  阅读(2368)  评论(0编辑  收藏  举报