Почему я получаю нарушение доступа к памяти при использовании потоков в Pybind11? - PullRequest
0 голосов
/ 12 апреля 2020

Я создал оболочку C++ для доступа к моим Python модулям. все работает, пока я не попытаюсь использовать потоки в моем приложении.
На моем модуле Python есть метод, который читает с веб-камеры (поэтому он использует бесконечное число l oop), и я посылаю обратные вызовы с C++ чтобы получить изображение и другую необходимую информацию из него.
Так как у нас есть метод блокировки, я решил использовать потоки.
Потоки в части Python, кажется, не работают на стороне C++, которая если я вызываю асин c часть счетчика webcam_feed l oop, ни один из моих обратных вызовов фактически не выполняется (на части 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:
enter image description here

То же самое происходит и так:


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

1 Ответ

0 голосов
/ 18 апреля 2020

Причиной нарушения доступа к памяти была попытка запустить методы с использованием разных потоков. То есть все связанные с 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; 
}
...