Я пишу расширение C для Python, которое должно освободить Global Interpreter Lock, пока оно работает с данными. Я думаю, что достаточно хорошо понял механизм GIL, но остается один вопрос: могу ли я получить доступ к данным в объекте Python, пока поток не владеет GIL? Например, я хочу читать данные из (большого) массива NumPy в функции C, в то время как я все еще хочу позволить другим потокам делать другие вещи на других ядрах процессора. Функция C должна
- отпустите GIL с
Py_BEGIN_ALLOW_THREADS
- чтение и обработка данных без использования функций Python
- даже записывать данные в ранее созданные массивы NumPy
- повторно получить GIL с
Py_END_ALLOW_THREADS
Это безопасно? Конечно, другие потоки не должны изменять переменные, которые использует функция C. Но, возможно, есть один скрытый источник ошибок: может ли интерпретатор Python переместить объект, например. какой-то сборкой мусора, в то время как функция C работает над этим в отдельном потоке?
Чтобы проиллюстрировать вопрос на минимальном примере, рассмотрим (минимальный, но полный) код ниже. Скомпилируйте его (в Linux) с помощью
gcc -pthread -fno-strict-aliasing -DNDEBUG -g -fwrapv -fPIC -I/usr/lib/pymodules/python2.7/numpy/core/include -I/usr/include/python2.7 -c gilexample.c -o gilexample.o
gcc -pthread -shared gilexample.o -o gilexample.so
и протестируйте его на Python с
import gilexample
gilexample.sum([1,2,3])
Безопасен ли код между Py_BEGIN_ALLOW_THREADS
и Py_END_ALLOW_THREADS
? Он обращается к содержимому объекта Python, и я не хочу дублировать (возможно, большой) массив в памяти.
#include <Python.h>
#include <numpy/arrayobject.h>
// The relevant function
static PyObject * sum(PyObject * const self, PyObject * const args) {
PyObject * X;
PyArg_ParseTuple(args, "O", &X);
PyObject const * const X_double = PyArray_FROM_OTF(X, NPY_DOUBLE, NPY_ALIGNED);
npy_intp const size = PyArray_SIZE(X_double);
double * const data = (double *) PyArray_DATA(X_double);
double sum = 0;
Py_BEGIN_ALLOW_THREADS // IS THIS SAFE?
npy_intp i;
for (i=0; i<size; i++)
sum += data[i];
Py_END_ALLOW_THREADS
Py_DECREF(X_double);
return PyFloat_FromDouble(sum);
}
// Python interface code
// List the C methods that this extension provides.
static PyMethodDef gilexampleMethods[] = {
{"sum", sum, METH_VARARGS},
{NULL, NULL, 0, NULL} /* Sentinel - marks the end of this structure */
};
// Tell Python about these methods.
PyMODINIT_FUNC initgilexample(void) {
(void) Py_InitModule("gilexample", gilexampleMethods);
import_array(); // Must be present for NumPy.
}