python - 使用pybind11在C++中涉及多线程的问题

标签 python c++ multithreading pybind11

对于一门类(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/

相关文章:

python - 如何在不同的分隔符上拆分字符串,但保留某些所述分隔符的输出? (标记字符串)

python - 仅关键字参数的替代名称 "in_"

c++ - C++ 宏展开的一个问题

java - 如何通过JNI将java CharSequence转换为c++ char*

c++ - 尝试将 qobject_cast 用于 Const 指针时的编译问题

python - 如何在 tensorflow 自定义训练循环中考虑 l1 和 l2 正则化器?

python - py2neo graph.merge() 的行为与 Cypher MERGE 不同?

c# - 线程无法访问对象

java - 在Java中,运行main的线程是否必须与静态内容初始化相同?

python-3.x - threading.Condition 和 threading.Event 之间的区别