python的线程和gil_Python GIL和线程(Python GIL and threads)

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

...

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值