Глобальная блокировка интерпретатора и доступ к данным (например, для массивов NumPy) - PullRequest
7 голосов
/ 11 января 2012

Я пишу расширение 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.
}

Ответы [ 2 ]

6 голосов
/ 11 января 2012

Это безопасно?

Строго, нет.Я думаю, что вы должны переместить вызовы на PyArray_SIZE и PyArray_DATA вне блока без GIL;если вы сделаете это, вы будете работать только с данными C.Возможно, вы также захотите увеличить счетчик ссылок на объект перед переходом в блок без GIL и затем уменьшить его.

После ваших изменений это должно быть безопасно.Не забудьте потом уменьшить счетчик ссылок.

4 голосов
/ 11 января 2012

Могу ли я получить доступ к данным в объекте Python, пока поток не владеет GIL?

Нет, вы не можете.

...