OpenMP/Pybind11:访问for循环中的python对象返回内部字符串错误

OpenMP/Pybind11:访问for循环中的python对象返回内部字符串错误,python,c++,vector,openmp,pybind11,Python,C++,Vector,Openmp,Pybind11,我试图在C++中使用pybDun11在Python对象列表中使用OpenMP。我在Python对象的std::vector中转换这个列表(如中所述),然后尝试在并行for循环中访问它们。但是,在for循环中调用向量中任何python对象的属性时,我会得到错误: Fatal Python error: deletion of interned string failed Thread 0x00007fd282bc7700 (most recent call first): Process fini

我试图在C++中使用pybDun11在Python对象列表中使用OpenMP。我在Python对象的std::vector中转换这个列表(如中所述),然后尝试在并行for循环中访问它们。但是,在for循环中调用向量中任何python对象的属性时,我会得到错误:

Fatal Python error: deletion of interned string failed
Thread 0x00007fd282bc7700 (most recent call first):
Process finished with exit code 139 (interrupted by signal 11: SIGSEGV)
我的问题是:什么是删除实习字符串错误?如何在OpenMP中避免它

我已经读到,问题是关于字符串的副本,所以我尝试用指针引用字符串,但没有帮助。此外,问题并不是来自Pybind中的转换问题,因为如果我删除#pragma omp子句,代码就可以完美地工作

任何帮助都将不胜感激

C++代码

#include <pybind11/pybind11.h>
#include <pybind11/numpy.h>
#include <pybind11/stl.h>
#include <omp.h>
#include <chrono>
#include <thread>

namespace py = pybind11;

py::object create_seq(
  py::object self
  ){

  std::vector<py::object> dict = self.cast<std::vector<py::object>>();

  #pragma omp parallel for
  for(unsigned int i=0; i<dict.size(); i++) {
    dict[i].attr("attribute") = 2;
  }

  return self;
}

PYBIND11_MODULE(error, m){

    m.doc() = "pybind11 module for iterating over generations";

    m.def("create_seq", &create_seq,
      "the function which creates a sequence");

}
import error

class test():
    def __init__(self):
        self.attribute = None

if __name__ == '__main__':
    dict = {}
    for i in range(50):
        dict[i] = test()
    pop = error.create_seq(list(dict.values()))
汇编时使用:

g++ -O3 -Wall -shared -std=c++14 -fopenmp -fPIC `python3 -m pybind11 --includes` openmp.cpp -o error.so
如果不持有全局解释器锁(GIL),就无法可靠地调用任何Python C-API代码(pybind11的基础)。在OpenMP循环中为每个线程上的每次访问处理GIL将有效地序列化循环,但现在增加了开销,因此它将比最初串行运行要慢

至于内部字符串:Python解释器保存常见的不可变对象,例如某些字符串和小整数,以防止它们被反复创建。这种常见的字符串被称为“interned”,这通常发生在后台(尽管您可以使用
PyString\u InternFromString
/
PyUnicode\u InternFromString
)添加自己的字符串)。由于这些是设计上的单例对象(毕竟这是它们的目的),因此只有一个线程应该创建/删除它们。

如果不持有全局解释器锁(GIL),就无法可靠地调用任何Python C-API代码(pybind11的基础)。在OpenMP循环中为每个线程上的每次访问处理GIL将有效地序列化循环,但现在增加了开销,因此它将比最初串行运行要慢


至于内部字符串:Python解释器保存常见的不可变对象,例如某些字符串和小整数,以防止它们被反复创建。这种常见的字符串被称为“interned”,这通常发生在后台(尽管您可以使用
PyString\u InternFromString
/
PyUnicode\u InternFromString
)添加自己的字符串)。由于这些是设计上的单线程对象(毕竟这是它们的目的),因此只有一个线程应该创建/删除它们。

我能够找到解决方案,但我认为我只是在用多个线程执行单线程工作。我使用了按以下方式排序的#pragma omp:

std::vector<py::object> dict = self.cast<std::vector<py::object>>();
  #pragma omp parallel for ordered schedule(dynamic)
  for(unsigned int i=0; i<dict.size(); i++) {
    py::object genome = dict[i];
    std::cout << i << std::endl;
    #pragma omp ordered
    genome.attr("fitness")=2; 
    }
std::vector dict=self.cast();
#用于有序计划的pragma omp并行(动态)

对于(unsigned int i=0;i我能够找到一个解决方案,但我认为我只是在用多个线程进行单线程工作。我使用了一个#pragma omp,按以下方式排序:

std::vector<py::object> dict = self.cast<std::vector<py::object>>();
  #pragma omp parallel for ordered schedule(dynamic)
  for(unsigned int i=0; i<dict.size(); i++) {
    py::object genome = dict[i];
    std::cout << i << std::endl;
    #pragma omp ordered
    genome.attr("fitness")=2; 
    }
std::vector dict=self.cast();
#用于有序计划的pragma omp并行(动态)

对于(unsigned int i=0;非常感谢您的回答。我不知道Python中的GIL问题。因此,如果我理解正确,由于任何Python对象都是由解释器一次访问的,因此不可能完全利用多线程。所谓的多线程实际上只是让不同的线程进行复制一个单线程的工作。我发现这非常惊人。使用Python线程时更糟糕:通常的策略是使用多处理()、子解释程序(),或者只在纯C++代码上使用线程。还有框架(例如)。我想,是用C++编写的主程序,用C++的代码嵌入了一些python函数。问题是:如果我用C++代码处理Python对象,我是否也会被吉尔限制?如果我控制线程,我不这么认为。我的C++代码,而不是Python。如果我错了请纠正我。对Python C-API或任何Python对象的访问都需要吉尔控制。当然,如果你知道没有其他Python线程,并且你控制C++级别的访问,那么你就很好。这两个警告是Python错误处理。(b/c每个Python线程只有一个全局变量,因此只有一个线程应该设置它)和信号处理(Python截取一些常见的变量来设置异常,例如SIGINT来设置KeyboardInterrupt,但信号和线程无论如何都不应该混合)。非常感谢您的回答。我不知道Python中的GIL问题。因此,如果我理解正确,由于任何Python对象都是由解释器一次访问的,因此不可能完全利用多线程。所谓的多线程实际上就是让不同的线程复制单个线程我发现这很惊人。使用Python线程时情况更糟:通常的策略是使用多处理()、子解释程序(),或者只在纯C++代码上使用线程。还有框架(例如)我想,是用C++编写的主程序,用C++的代码嵌入了一些python函数。问题是:如果我用C++代码处理Python对象,我是否也会被吉尔限制?如果我控制线程,我不这么认为。我的C++代码,而不是Python。如果我错了请纠正我。对Python C-API或任何Python对象的访问都需要吉尔控制。当然,如果你知道没有其他Python线程,并且你控制C++级别的访问,那么你就很好。这两个警告是Python错误处理。(b/c这是一个单一的全球va