为什么在 Pybind11 中使用线程时会发生内存访问冲突?

Why do I get memory access violation when using threads in Pybind11?

我创建了一个 C++ 包装器来访问我的 Python 模块。一切正常,直到我尝试在我的应用程序中使用线程。
在我的 Python 模块上有一个从网络摄像头读取的方法(因此它使用无限循环),我从 C++ 发送回调以从中获取图像和其他所需信息。
由于我们这里有一个阻塞的方法,所以我决定使用线程。
Python 部分的线程似乎在 C++ 端不起作用,也就是说,如果我调用 webcam_feed 循环的异步计数器部分,我的回调 none 实际上是已执行(在 python 部分,例程都已执行,但是,它似乎没有以某种方式到达 C++ 部分。我在 C++ 方面没有得到任何反馈,但是,在 Python 部分,那些负责执行回调的例程将信息保存到磁盘,因此我确定它们已被执行)。
我问了一个单独的问题 .
因此我决定在 C++ 客户端中使用线程。但是,每当我执行代码(如下所示)时,每当我想在线程启动后使用任何方法时,都会遇到访问冲突。 以下是我目前拥有的示例回调:


void default_callback(bool status, std::string id, py::array_t<uint8_t>& img)
{
    auto rows = img.shape(0);
    auto cols = img.shape(1);
    auto type = CV_8UC3;

    cv::Mat img1(rows, cols, type, img.mutable_data());

    cv::imshow("from callback", img1);
    cv::waitKey(1);
    auto timenow = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
    std::cout << "\narg1: " << status << " arg2: " << id << " arg3: " << typeid(img).name() << " " << ctime(&timenow) << std::endl;
}

void default_c_callback_temporary(bool status, char* message)
{
    std::cout << "status is: " << status << " id/name: " << message << " ptr:" << "" << std::endl;
    std::ofstream myfile;
    myfile.open("example.txt");
    myfile << "Writing this to a file: " << status << message << std::endl;
    myfile.close();
}

这是实际测试

void thread_test_start(Core* core)
{
    try
    {
        core->SetCpuAffinity(2);
        core->AddCallback(default_callback);
        core->AddCallback_C_tmp(default_c_callback_temporary);
        //set true to run the async version (implemented in python)
        core->Start(false);
    }
    catch (const std::exception& ex)
    {
        std::cout << ex.what() << std::endl;
    }
}


int main()
{
    Core* core = new Core(false);
    std::thread t(thread_test_start, core);

    py::print(core->GetCallbacks());
    std::cout << "\nGet C Callbacks:\n";
    py::print(core->GetCallbacks_C_tmp());

    std::cout << "\nEverything done. press Enter to Exit";
    t.join();
    std::getchar();
    return 0;
}

调用core->GetCallbacks()导致内存访问冲突:

Exception thrown at 0x000000006FCC6D80 (python36.dll) in TestDLL.exe: 0xC0000005: Access violation reading location 0x0000000000000010.

这是显示 VS2019 内部访问冲突错误的快照:

这样做也是一样的:


void thread_test_start2()
{
    try
    {
        Core* core = new Core(false);
        core->SetCpuAffinity(2);
        core->AddCallback(default_callback);
        core->AddCallback_C_tmp(default_c_callback_temporary);

        std::thread t(&Core::Start, core, false);
        py::print(core->GetCallbacks());
        std::cout << "\nGet C Callbacks:\n";
        py::print(core->GetCallbacks_C_tmp());
        t.join();
    }
    catch (const std::exception& ex)
    {
        std::cout << ex.what() << std::endl;
    }
}

结果:

Exception thrown at 0x000000006FCC0CDF (python36.dll) in TestDLL.exe: 0xC0000005: Access violation writing location 0x0000000000000020.

喜欢前一个。 为什么会出现此错误?我们不能在 Pybind11 中使用线程吗?我在这里错过了什么?

这是一个重现此问题的示例项目:https://workupload.com/file/6LmfRtbztHK

内存访问冲突的原因是尝试 运行 使用不同线程的方法。也就是说,所有与 Pybind11 相关的方法(使用 Pybind11 的方法)都需要在看起来非常相同的线程下执行。
因此在一个线程下执行部分代码并试图在主线程中执行一些其他方法将导致内存访问冲突。
为了解决这个问题,我最终在一个回调中实现了一个简单的调度程序,其中任何需要 运行 的方法首先设置一个标志,然后每次回调是 运行,标志是勾选对应的方法为运行。

int flag=0;
void callback(...)
{
    switch(flag)
    {
        case 1: //e.g. stop
            core->stop();
            break;
        case 2: // e.g. get_callbacks() 
            core->get_callbacks();
            break;
        case 3: 
            //some other op
            break;
        ....     
    }
    //reset flag
    flag = 0; 
}