我正在开发一个 Python 项目,出于性能原因,我需要将一些计算卸载到 C++。我正在使用 Pybind11 在 Python 和 C++ 之间建立桥梁。在我的场景中,我尝试运行多个 C++ 线程,这些线程调用回调函数来修改 Python 对象中的值。但是,我遇到了僵局,我不知道为什么。这是我的代码的简化版本:
Python代码:
import example
import threading
class MyClass:
def __init__(self, value=0):
self.value = value
self.lock = threading.Lock()
def python_callback(self, arg1: float, arg2: float) -> float:
with self.lock:
self.value += arg1
self.value += arg2
return float(self.value)
myclass = MyClass()
def cpp_thread():
example.call_cpp_thread(myclass, 1.0, 1.0, 10000)
def python_thread():
for i in range(40000):
myclass.python_callback(1, 1)
# Create and start threads
thread1 = threading.Thread(target=cpp_thread)
thread2 = threading.Thread(target=python_thread)
thread1.start()
thread2.start()
# Wait for threads to complete
thread1.join()
thread2.join()
print(myclass.value)
C++代码:
#include <pybind11/pybind11.h>
#include <pybind11/functional.h>
#include <pybind11/stl.h>
#include <thread>
namespace py = pybind11;
void call_python_callback(py::object obj, float arg1, float arg2, int calltime) {
for (int i = 0; i < calltime; i++) {
obj.attr("python_callback")(arg1, arg2);
}
}
void call_cpp_thread(py::object obj, float arg1, float arg2, int calltime) {
std::thread t1(call_python_callback, obj, arg1, arg2, calltime);
std::thread t2(call_python_callback, obj, arg1, arg2, calltime);
std::thread t3(call_python_callback, obj, arg1, arg2, calltime);
std::thread t4(call_python_callback, obj, arg1, arg2, calltime);
t1.join();
t2.join();
t3.join();
t4.join();
}
PYBIND11_MODULE(example, m) {
m.def("call_python_callback", &call_python_callback, "Call a Python callback",
py::arg("obj"), py::arg("arg1"), py::arg("arg2"), py::arg("calltime"));
m.def("call_cpp_thread", &call_cpp_thread, "Call cpp thread",
py::arg("obj"), py::arg("arg1"), py::arg("arg2"), py::arg("calltime"));
}
说明: Python 类 MyClass:它有一个方法 python_callback 可以修改对象的值。该方法使用threading.Lock进行保护,以确保属性值的线程安全。
C++函数call_python_callback:重复调用Python回调函数。调用python代码时会自动获取GIL
C++函数call_cpp_thread:它启动多个执行call_python_callback的线程。 问题:
当我运行代码时,会导致死锁。
我尝试在 cpp 中添加互斥锁来保护对 python 的调用。
问题是这样的 当python调用cpp代码时。它尝试在 cpp 中创建线程。 cpp的主线程持有GIL并等待子线程完成(因为我在所有线程上调用.join函数)。
在子线程中需要GIL来执行python回调。
这导致了僵局。
如何解决: 在加入线程之前释放吉尔。