使用 Python 和 C api 进行多线程

Posted

技术标签:

【中文标题】使用 Python 和 C api 进行多线程【英文标题】:Multithreading with Python and C api 【发布时间】:2015-04-12 22:10:08 【问题描述】:

我有一个 C++ 程序,它使用 C api 来使用我的 Python 库。 Python 库和 C++ 代码都是多线程的。

特别是,C++ 程序的一个线程实例化了一个继承自threading.Thread 的 Python 对象。我需要我的所有 C++ 线程能够调用该对象上的方法。

从我的第一次尝试(我天真地只是从主线程实例化对象,然后等待一段时间,然后调用该方法)我注意到与刚刚创建的对象关联的 Python 线程的执行一旦停止执行回到 C++ 程序。

如果继续使用 Python 执行(例如,如果我调用 PyRun_SimpleString("time.sleep(5)");),则 Python 线程会在后台继续执行,一切正常,直到等待结束并返回 C++。

我显然做错了什么。我应该怎么做才能使我的 C++ 和 Python 成为多线程的 both 并能够很好地相互协作?我之前没有该领域的经验,所以请不要假设任何事情!

【问题讨论】:

【参考方案1】:

执行您要执行的操作的正确步骤顺序是:

在主线程中:

    使用Py_Initialize*初始化Python。 使用 PyEval_InitThreads() 初始化 Python 线程支持。 启动 C++ 线程。

此时,主线程仍然持有 GIL。

在 C++ 线程中:
    使用PyGILState_Ensure() 获取 GIL。 创建一个新的 Python 线程对象并启动它。 使用 PyGILState_Release() 发布 GIL。 睡觉,做一些有用的事情或退出线程。

由于主线程持有 GIL,该线程将等待获取 GIL。如果主线程调用 Python API,它可能会不时释放 GIL,让 Python 线程执行一段时间。

回到主线程:
    释放 GIL,使线程能够使用PyEval_SaveThread() 运行 在尝试使用其他 Python 调用之前,请使用 PyEval_RestoreThread() 重新获取 GIL

我怀疑你错过了最后一步——在主线程中释放 GIL,让 Python 线程执行。

我在this link 有一个小而完整的示例。

【讨论】:

【参考方案2】:

当你从python的threading.Thread回调时,你可能没有解锁Global Interpreter Lock。

好吧,如果你使用的是裸 python 的 C API,你有 some documentation here,关于如何释放/获取 GIL。但是在使用 C++ 时,我必须警告你,它可能会在你的 C++ 代码中抛出任何异常时崩溃。 See here.

一般来说,任何运行时间过长的 C++ 函数都应该解锁 GIL 并锁定,只要它再次使用 C Python API。

【讨论】:

很抱歉,我似乎没有正确理解这一点。这是我所做的:1)PyGILState_STATE gstate; gstate = PyGILState_Ensure(); 2)创建从threading.Thread 继承的 python 对象 3)PyGILState_Release(gstate); 4)休眠几秒钟(在 C++ 中)在运行函数中,对象应该打印一次在一段时间内,但它显然只在最初的几毫秒内这样做。我在做什么错..?

以上是关于使用 Python 和 C api 进行多线程的主要内容,如果未能解决你的问题,请参考以下文章

Python 多进程多线编程模板

写出java多线程程序设计中常用类及方法名,并分别说明它们的作用。

python中的线程安全和非线程安全的区别

Python 多线程效率不高吗

通过网络图片小爬虫对比Python中单线程与多线(进)程的效率

使用python多线程进行简单的性能测试