对于一门类(class),我不得不用C++编写一个使用对象的程序,该程序使用两个线程让两个函数相互通信:函数1将'a'添加到队列中,然后函数2从队列中弹出'a'。队列和函数1再次添加“b”,两个函数在不同的线程中运行。幸运的是,我编写的程序可以正常工作。
现在,我想使用Pybind11从Python脚本调用C++对象。我希望这不会造成任何问题,因为多线程发生在已经起作用的C++代码中。不幸的是,事实并非如此,不可预测的事情开始发生。
#include <iostream>
#include <thread>
#include <chrono>
#include <queue>
#include <random>
#include <mutex>
#include <pybind11/pybind11.h>
class Communication{
private:
std::mutex m1;
std::queue<double> _q;
double lb = 0;
double ub = 10000;
int _t_max;
int _max_it;
bool _finish = 0;
unsigned int initial_length;
public:
Communication(int t_max = 3000, int max_it = 10){
//Upon creating the Communication object, a queue is made and a maximum iteration length (max_it) is given. t_max is the maximum 'sleeping time' between adding a random number to the queue.
initial_length = _q.size();
_t_max = t_max;
_max_it = max_it;
std::thread t1(&Communication::produce, this);
std::thread t2(&Communication::consume, this);
t1.join();
t2.join();
}
void produce(){ //The functions which adds a random number, between 0 and 10000, to the queue. In this process, the program 'sleeps' for a time between 0 and 3000ms.
//The randomizers for the to-be-added integer, and the sleeping time
std::uniform_real_distribution<double> unif(lb, ub);
std::default_random_engine re;
std::mt19937 mt;
std::uniform_int_distribution<> distr(0., _t_max);
for(int i = 0; i < _max_it; i++){
int n = distr(mt);
double rnd = unif(re);
m1.lock();
_q.push(rnd);
std::cout << rnd << " is added to the queue." << std::endl;
m1.unlock();
}
//When all the iterations have been done, the consumer needs to be told that the program is finished, by putting _finish to true.
_finish = true;
}
void consume(){//The function which pops the front element of the queue.
while(!_finish){
if (_q.size() > initial_length){//If an element is pushed (in void produce()), pop() it in this function.
m1.lock();
_q.pop();
m1.unlock();
}
}
}
};
//The module required to use the object in Python
namespace py = pybind11;
PYBIND11_MODULE(Communication, m){
py::class_<Communication>(m, "Communication")
.def(py::init<int, int>());
};
如果我在Python中使用此对象,那么produce()函数会继续向队列中添加元素,而consume()函数不会再弹出它们。而且,程序不会完成,因为while(!_ finish)永远不会返回!true,即使它应该这样做也是如此。我已经检查了是否通过(在它们之间放置cout)调用了invoke()函数中的while循环和if语句。奇怪的是,这个提示某种程度上触发了if语句一次为真,然后程序再次冻结。我一直在阅读有关GIL的其他几个主题,当您想使用多个线程时,它们可能会引起问题。我曾尝试在多个地方添加发布和获取GIL锁,但是不幸的是,这并不能解决问题。有谁知道这里出了什么问题或在哪里可以找到问题的答案?
在此先谢谢您。
备注:
这是我用来检查在python中调用C++对象的代码:
import Communication
p = Communication.Communication(300, 10)
最佳答案
哈利和格林的结合在这里解决了这个问题。确实存在调用_finish和_q.size()的竞争条件。通过将m1.lock()和m1.unlock()添加到适当的位置,进程变得互斥并导致正确的行为。非常感谢!
关于python - 使用pybind11在C++中涉及多线程的问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64503816/