Пытаюсь из кода на 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++ , ни с питоновским кодом, хотя вызовы действительно производятся из разных потоков. Что я делаю нетак?
Потоки 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
Айфон мало держит заряд, разбираемся с проблемой вместе с AppLab
Перевод документов на английский язык: Важность и ключевые аспекты
Хочу добавить некоторые требования к паролю:
Я делаю новостную афишу и хочу чтобы блоки и информацией шли в три ряда то есть 3x3 но когда я добовляю 4 блок получается что он уходит левее