全局解释器锁定和访问数据(例如,对于NumPy阵列)

时间:2012-01-11 18:36:25

标签: python numpy python-c-api

我正在为Python编写一个C扩展,它应该在对数据进行操作时释放全局解释器锁。我认为我已经很好地理解了GIL的机制,但仍然存在一个问题:当线程不拥有GIL时,我可以访问Python对象中的数据吗?例如,我想从C函数中的(大)NumPy数组中读取数据,而我仍然希望允许其他线程在其他CPU内核上执行其他操作。 C函数应该

  • 使用Py_BEGIN_ALLOW_THREADS
  • 发布GIL
  • 在不使用Python函数的情况下阅读和处理数据
  • 甚至将数据写入先前构建的NumPy数组
  • 使用Py_END_ALLOW_THREADS
  • 重新获取GIL

这样安全吗?当然,其他线程不应该改变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_THREADSPy_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 个答案:

答案 0 :(得分:6)

  

这样安全吗?

严格地说,没有。我认为你应该将调用移到GIL-less块之外的PyArray_SIZEPyArray_DATA;如果你这样做,你将只对C数据进行操作。您可能还希望在进入无GIL块之前增加对象的引用计数,然后再减少它。

编辑完成后,应该是安全的。不要忘记之后递减引用计数。

答案 1 :(得分:4)

  

当线程不拥有GIL时,我可以访问Python对象中的数据吗?

不,你不能。