Параллельный вызов boost.python

115
23 апреля 2021, 06:00

Пытаюсь из кода на python, распараллеленного с помощью ThreadPoolExecutor вызвать код на C++. Создается впечатление, что в момент входа в C++ функцию все питоновские потоки замораживаются. Пример:

#include <boost/python.hpp>
#include <iostream>
#include <iomanip>
#include <thread>
#include <chrono>
using namespace std;
using namespace std::chrono;
using namespace std::this_thread;
using namespace boost::python;
void foo(int i)
{
    cout<<"Enter " << " foo("<<i<<") (c++) thread: " << get_id() << endl;
    sleep_for(  seconds(2) );
    cout<<"Exit  " << " foo("<<i<<") (c++) thread: " << get_id() << endl;
}
BOOST_PYTHON_MODULE( test_py_async )
{
    def("foo", &foo);
}

python:

import test_py_async
import concurrent.futures
N=8
def foo(i):
    print( f"Enter  foo({i}) (py)")
    test_py_async.foo(i)
    print( f"Exit   foo({i}) (py)")
    return i
with concurrent.futures.ThreadPoolExecutor(max_workers=N) as executor:
    print("submit tasks:")
    futures=[ executor.submit( foo, i ) for i in range(N) ]
    print("wait for tasks:")
    results=[ future.result() for future in futures ]
    print(f"results: {results}")
print(f"exit ThreadPoolExecutor")
assert( results == [ i for i in range(N) ] )

вывод:

submit tasks:
Enter  foo(0) (py)
Enter  foo(0) (c++) thread: 11796
Exit   foo(0) (c++) thread: 11796
Enter  foo(1) (py)
Enter  foo(1) (c++) thread: 16580
Exit   foo(1) (c++) thread: 16580
Exit   foo(0) (py)
Enter  foo(2) (py)
Enter  foo(2) (c++) thread: 20240
Exit   foo(2) (c++) thread: 20240
Enter  foo(3) (py)
Exit   foo(1) (py)
Enter  foo(3) (c++) thread: 11796
Exit   foo(3) (c++) thread: 11796
Exit   foo(2) (py)
Enter  foo(4) (py)
Enter  foo(4) (c++) thread: 17568
Exit   foo(4) (c++) thread: 17568
Enter  foo(6) (py)
Enter  foo(6) (c++) thread: 19672
Exit   foo(6) (c++) thread: 19672
Enter  foo(5) (py)
Enter  foo(7) (py)
Enter  foo(7) (c++) thread: 16580
Exit   foo(7) (c++) thread: 16580
wait for tasks:
Exit   foo(3) (py)
Exit   foo(6) (py)
Exit   foo(7) (py)
Exit   foo(4) (py)
Exit   foo(5) (c++) thread: 16804
Exit   foo(5) (py)
results: [0, 1, 2, 3, 4, 5, 6, 7]
exit ThreadPoolExecutor

Из лога видно, что вызовы c++ не пересекаются по времени выполнения ни с другими вызовами c++ , ни с питоновским кодом, хотя вызовы действительно производятся из разных потоков. Что я делаю нетак?

Answer 1

Потоки python действительно в каком-то смысле замораживаются на время работы кода расширения, и причина тому – GIL

Поток, в котором выполняется функция foo, захватывает GIL, и не отпускает его пока выполнение функции не завершится.

Если бы ваш код вызывал sleep из модуля time, или обращался к специфичным для python процедурам ввода/вывода, или иным образом взаимодействовал с интерпретатором, GIL в какой-то момент мог быть освобожден, давая возможность другим потокам полноценно работать.

Освободить его можно и вручную, обрамив ваш код специальными макросами.

Py_BEGIN_ALLOW_THREADS
sleep_for(  seconds(2) );
Py_END_ALLOW_THREADS

Примерно то же самое происходит и в time.sleep

Такой код не должен взаимодействовать с объектами python и/или обращаться к API.
Подробнее это описано в документации

submit tasks:
Enter  foo(0) (py)
Enter  foo(0) (c++) thread: 140388371265280
Enter  foo(1) (py)
Enter  foo(1) (c++) thread: 140388362872576
Enter  foo(2) (py)
Enter  foo(2) (c++) thread: 140388354479872
Enter  foo(3) (py)
Enter  foo(3) (c++) thread: 140388346087168
Enter  foo(4) (py)
Enter  foo(4) (c++) thread: 140388337694464
Enter  foo(5) (py)
Enter  foo(5) (c++) thread: 140387985389312
Enter  foo(6) (py)
Enter  foo(6) (c++) thread: 140387976996608
Enter  foo(7) (py)
Enter  foo(7) (c++) thread: 140387968603904
wait for tasks:
Exit   foo(0) (c++) thread: 140388371265280
Exit   foo(0) (py)
Exit   foo(1) (c++) thread: 140388362872576
Exit   foo(1) (py)
Exit   foo(2) (c++) thread: 140388354479872
Exit   foo(2) (py)
Exit   foo(3) (c++) thread: 140388346087168
Exit   foo(3) (py)
Exit   foo(4) (c++) thread: 140388337694464
Exit   foo(4) (py)
Exit   foo(5) (c++) thread: 140387985389312
Exit   foo(5) (py)
Exit   foo(6) (c++) thread: 140387976996608
Exit   foo(6) (py)
Exit   foo(7) (c++) thread: 140387968603904
Exit   foo(7) (py)
results: [0, 1, 2, 3, 4, 5, 6, 7]
exit ThreadPoolExecutor
READ ALSO
jQuery Validate plugin - не срабатывает правило для пароля

jQuery Validate plugin - не срабатывает правило для пароля

Хочу добавить некоторые требования к паролю:

98
Как мне сделать чтобы блоки шли в 3 ряда а потом опускалися в низ и опять в три ряда

Как мне сделать чтобы блоки шли в 3 ряда а потом опускалися в низ и опять в три ряда

Я делаю новостную афишу и хочу чтобы блоки и информацией шли в три ряда то есть 3x3 но когда я добовляю 4 блок получается что он уходит левее

93