Python GIL和线程(Python GIL and threads)
我在我的大C ++应用程序中嵌入了Python3。 Python为自定义数据处理提供了用户脚本功能。
问题 :我有许多与Python交互的线程,我真的不知道如何用GIL保护我的代码。 到目前为止,我使代码工作的唯一方法是使用boost::mutex 。
这是一个非常简单的示例,可以重现我的问题:
线程A首先调用Init()来初始化Python(静态函数)。
线程B调用Pythonize()在Python上做一些工作。 线程B在第一次调用时被阻塞以锁定GIL。
代码 :
#include
#include
#include
#include "Python.h"
struct RTMaps_GILLock
{
RTMaps_GILLock()
{
std::cout << "Locking..." << std::endl;
m_state = PyGILState_Ensure();
}
~RTMaps_GILLock()
{
std::cout << "Unlocking..." << std::endl;
PyGILState_Release(m_state);
}
private:
PyGILState_STATE m_state;
};
#define GILLOCK RTMaps_GILLock lock;
class PythonEmbed
{
public:
static void Init()
{
Py_Initialize();
// EDIT : adding those two lines made my day :
PyEval_InitThreads(); // This acquires GIL
PyEval_SaveThread(); // Release the GIL
}
void Pythonize()
{
GILLOCK;
// Never goes here :(
std::cout << "OK" << std::endl;
}
};
int main()
{
PythonEmbed::Init();
PythonEmbed pyt;
boost::thread t(boost::bind(&PythonEmbed::Pythonize, pyt));
t.join();
}
它在第一次锁定调用中死锁。 控制台显示:锁定......
永远不会打印“确定”。 我究竟做错了什么 ?
编辑:更正的代码,现在它正在工作。 我需要从主线程中释放GIL。
I have embedded Python3 in my big C++ application. Python gives the user script capability for custom data processing.
Problem : I have many threads that interact with Python and I don't really get how to protect my code with GIL. So far, the only way I made my code work is using boost::mutex.
Here is a very simplified example that reproduces my problem:
Thread A calls first Init() to initialize Python (static function).
Thread B calls Pythonize() to do some work on Python. Thread B is blocked on the first call for locking GIL.
Code:
#include
#include
#include
#include "Python.h"
struct RTMaps_GILLock
{
RTMaps_GILLock()
{
std::cout << "Locking..." << std::endl;
m_state = PyGILState_Ensure();
}
~RTMaps_GILLock()
{
std::cout << "Unlocking..." << std::endl;
PyGILState_Release(m_state);
}
private:
PyGILState_STATE m_state;
};
#define GILLOCK RTMaps_GILLock lock;
class PythonEmbed
{
public:
static void Init()
{
Py_Initialize();
// EDIT : adding those two lines made my day :
PyEval_InitThreads(); // This acquires GIL
PyEval_SaveThread(); // Release the GIL
}
void Pythonize()
{
GILLOCK;
// Never goes here :(
std::cout << "OK" << std::endl;
}
};
int main()
{
PythonEmbed::Init();
PythonEmbed pyt;
boost::thread t(boost::bind(&PythonEmbed::Pythonize, pyt));
t.join();
}
it is deadlocking in the first lock call. The console shows: Locking...
The "OK" is never printed. What am I doing wrong ?
EDIT : corrected code, now it is working. I needed to release the GIL from the main thread.
原文:https://stackoverflow.com/questions/29100082
更新时间:2020-10-07 16:10
最满意答案
我有你的确切问题,请确保不要从主线程调用PyGILState_Ensure(),初始化Pythons,因为它需要一个完全不同的调用。 我最后设置了一个线程映射器,每次调用我的acquirePython()都会检查调用它的线程,如果它是主线程,它使用:
PyEval_SaveThread();
否则它存储GIL。 这些是我班级的相关部分:
void MManager::acquirePython(void) {
MThread thisThread = MFramework::MProcesses::GetCurrentThread();
if (thisThread != mainThread) {
Lock();
std::map::iterator i = threadStates.find(thisThread);
if (i == threadStates.end()) {
Unlock();
PyGILState_STATE gstate = PyGILState_Ensure();
_PyGILState_STATE_* encState = new _PyGILState_STATE_;
encState->state = gstate;
encState->refCount = 1;
Lock();
threadStates[thisThread] = encState;
Unlock();
} else {
_PyGILState_STATE_* encState = (_PyGILState_STATE_*)i->second;
encState->refCount = encState->refCount + 1;
Unlock();
}
} else {
if (mainThreadState) PyEval_RestoreThread((PyThreadState*)mainThreadState);
}
}
void MManager::releasePython(void) {
MThread thisThread = MFramework::MProcesses::GetCurrentThread();
if (thisThread != mainThread) {
Lock();
std::map::iterator i = threadStates.find(thisThread);
if (i != threadStates.end()) {
_PyGILState_STATE_* encState = (_PyGILState_STATE_*)i->second;
if (encState->refCount <= 1) {
threadStates.erase(i);
Unlock();
PyGILState_Release(encState->state);
delete encState;
} else {
encState->refCount = encState->refCount - 1;
Unlock();
}
} else {
Unlock();
}
} else {
mainThreadState = PyEval_SaveThread();
}
}
I had your exact problem, be sure to not call PyGILState_Ensure() from the main thread, the one that initialize Pythons, because it needs a total different call. I've end setting a thread mapper, and each call to my acquirePython() checks what thread is calling it, if it's the main thread , it uses:
PyEval_SaveThread();
otherwise it stores the GIL. Those are the relevant sections of my class:
void MManager::acquirePython(void) {
MThread thisThread = MFramework::MProcesses::GetCurrentThread();
if (thisThread != mainThread) {
Lock();
std::map::iterator i = threadStates.find(thisThread);
if (i == threadStates.end()) {
Unlock();
PyGILState_STATE gstate = PyGILState_Ensure();
_PyGILState_STATE_* encState = new _PyGILState_STATE_;
encState->state = gstate;
encState->refCount = 1;
Lock();
threadStates[thisThread] = encState;
Unlock();
} else {
_PyGILState_STATE_* encState = (_PyGILState_STATE_*)i->second;
encState->refCount = encState->refCount + 1;
Unlock();
}
} else {
if (mainThreadState) PyEval_RestoreThread((PyThreadState*)mainThreadState);
}
}
void MManager::releasePython(void) {
MThread thisThread = MFramework::MProcesses::GetCurrentThread();
if (thisThread != mainThread) {
Lock();
std::map::iterator i = threadStates.find(thisThread);
if (i != threadStates.end()) {
_PyGILState_STATE_* encState = (_PyGILState_STATE_*)i->second;
if (encState->refCount <= 1) {
threadStates.erase(i);
Unlock();
PyGILState_Release(encState->state);
delete encState;
} else {
encState->refCount = encState->refCount - 1;
Unlock();
}
} else {
Unlock();
}
} else {
mainThreadState = PyEval_SaveThread();
}
}
2015-03-18
相关问答
将实现细节用作代码的核心功能是不好的做法。 GIL是CPython的实现细节,在其他实现中不存在。 使用旨在做你想做的事情。 It's bad practice to use an implementation detail as a core feature of your code. The GIL is an implementation detail of CPython, and doesn't exist in other implementations. Use things tha
...
将functor作为方法公开并不正式支持 。 支持的方法是公开一个委托给成员函数的非成员函数。 但是,这可能会导致大量的样板代码。 尽我所知,Boost.Python的实现并没有明确地排除函数,因为它允许将python::object实例作为方法公开。 但是,Boost.Python确实对作为方法公开的对象类型提出了一些要求: 仿函数是CopyConstructible。 函数是可调用的。 即实例o可以被称为o(a1, a2, a3) 。 呼叫签名在运行时必须作为元数据提供。 Boost.Pyth
...
使用GIL来保护你的Python代码是不安全的 - 很难知道你什么时候放弃GIL。 GIL在那里保护翻译,而不是你的代码。 您需要序列化字典的使用,最简单的方法是使用Lock对象。 from threading import Lock
dLock = Lock()
在thread1中: dLock.acquire()
dictDemo[callid]=val
dLock.release()
在thread2中: dLock.acquire()
for key in dictDemo.keys(
...
完全是偶然的,我发现了一个工具,就是这样: gil_load 。 它是在我发布问题后实际发布的。 干得好,@ chrisjbillington。 >>> import sys, math
>>> import gil_load
>>> gil_load.init()
>>> gil_load.start(output = sys.stdout)
>>> for x in range(1, 1000000000):
... y = math.log(x**math.pi)
[2017-03-
...
好吧,实际上,我认为答案是time.sleep()将屈服于其他等待的线程。 也许其他限制或担忧会及时发挥作用,但在这个阶段,性能不是问题,只是并发。 OK, so practically speaking, I think the answer is that time.sleep() will yield to other waiting threads. Perhaps other limitations or concerns will rear their heads in time, b
...
嵌入到c ++中的python是否允许你同时运行n个python脚本? 您根本无需执行任何操作即可同时运行多个Python 脚本 。 GIL是解释器的本地,因此如果您运行多个解释器进程,GIL将不会阻止它们同时执行。 如果这是一个拼写错误,你的意思是线程然后是,你可以从Python调用C或C ++代码来绕过GIL。 有关相关API的详细信息,请参阅线程状态和全局解释器锁 。 can python embedded into c++ allow you to run n python scripts
...
多线程的一个主要原因是程序可以利用多个CPU(和/或CPU上的多个核心)来计算每秒更多的操作。 但是在Python中,GIL意味着即使你有多个线程在计算上同时进行操作,这些线程中只有一个实际上会在任何给定的时刻运行,因为所有其他线程都将被阻塞,等待获取全局解释器锁。 这意味着多线程Python程序实际上比单线程版本要慢 ,而不是更快,因为一次只能运行一个线程 - 另外还有迫使每个线程等待,获取和然后每隔几毫秒放弃GIL(循环风格)。 为了证明这一点,这里是一个玩具Python脚本,它产生指定数量的
...
不要担心GIL。 根据程序的类型(计算与I / O),您将拥有不同的性能特征。 如果您的程序受I / O限制,那么您可能根本不会注意到GIL。 另一种方法是使用多处理模块,其中每个进程在其自己的OS进程中运行,并具有自己的Python运行时。 您可以使用此方法充分利用多个内核,并且通常更安全,因为您不必担心同步对共享内存的访问。 Don't worry about the GIL. Depending on the kinds of things your program does (calcul
...
Django的save()对GIL没什么特别的。 实际上,在Python代码中几乎没有任何关于GIL的功能 - 当它执行时,线程必须保存GIL。 在save()中只有两种方式可以释放GIL: Python决定切换线程(在sys.getcheckinterval()指令之后) Django调用实现的数据库接口例程来释放GIL 第二点可能是您正在寻找的 - 执行SQL COMMIT ,在执行期间,SQL后端释放GIL。 但是,这取决于SQL接口,我不确定流行的实际上是否发布了GIL *。 而且, sa
...
我有你的确切问题,请确保不要从主线程调用PyGILState_Ensure(),初始化Pythons,因为它需要一个完全不同的调用。 我最后设置了一个线程映射器,每次调用我的acquirePython()都会检查调用它的线程,如果它是主线程,它使用: PyEval_SaveThread(); 否则它存储GIL。 这些是我班级的相关部分: void MManager::acquirePython(void) {
MThread thisThread = MFramework::MProces
...