在CPython C扩展类型上属性读/写之前获取锁定

问题描述 投票:1回答:1

我正在为CPython写一个多线程C扩展。我有一个用简单的libuv rwlock使线程安全的POD。我想包装它,以便可以分配它并通过简单的PyMemberDef从Python访问数据。我的问题是,仅获取/释放getattrosetattro中的锁就足够了吗?还是我错过了一些东西?我是CPython API的新手,因此可以随时完全推荐其他方法。

我正在尝试做的简化示例:

#include <Python.h>
#include <uv.h>

typedef struct Pod_s {
  uv_rwlock_t lock;
  int number;
} Pod;

typedef struct PyPod_s {
    PyObject_HEAD
    Pod pod;
} PyPod;

static PyMemberDef[] PyPod_members = {
  {"number", T_INT, offsetof(PyPod, pod) + offsetof(Pod, number)},
  {0},
};

static PyObject *PyPod_new(PyTypeObject *type, PyObject *args, PyObject *kwds) {
  PyPod *self;
  self = (PyPod *) type->tp_alloc(type, 0);
  if(self != NULL)
    uv_rwlock_init(&self->pod.lock);  // In real life this would be error-checked
  return (PyObject *) self;
}

// Is this getattro/setattro safe?
static PyObject *PyPod_getattro(PyObject *self, PyObject *attr) {
  PyPod *pypod = (PyPod *) self;
  uv_rwlock_rdlock(&pypod->pod.lock);
  PyObject *ret = PyObject_GenericGetAttr(self, attr);
  uv_rwlock_rdunlock(&pypod->pod.lock);
  return ret;
}

static int PyPod_setattro(PyObject *self, PyObject *attr, PyObject *value) {
  PyPod *pypod = (PyPod *) self;
  uv_rwlock_wrlock(&pypod->pod.lock);
  int ret = PyObject_GenericSetAttr(self, attr, value);
  uv_rwlock_wrunlock(&pypod->pod.lock);
  return ret;
}

static PyTypeObject PyPodType = {
  PyObject_HEAD_INIT(NULL)
  .tp_name = "PodModule.Pod",
  .tp_basicsize = sizeof(PyPod),
  .tp_flags = Py_TPFLAGS_DEFAULT,
  .tp_new = PyPod_new,
  .tp_members = PyPod_members,
  .tp_getattro = PyPod_getattro,
  .tp_setattro = PyPod_setattro,
};
python c multithreading cpython
1个回答
0
投票

如果其他人尝试这样做:是的,这是线程安全的对于您的C代码

这里未提出的问题是Python解释器如何?如果仅主线程(调用您的扩展程序的解释器)曾经回调/返回到解释器,那么您就无需担心。

但是,如果C扩展中产生的线程能够调用解释器,则需要考虑GIL。首先释放GIL并使用PyEval_SaveThread()获取主线程的状态。然后使用存储在线程状态中的interp *来使用PyThreadState_New(interp)旋转新的线程状态,这是您打算在C中创建的每个线程的状态。最后,在调用Python解释器之前,请使用PyEval_RestoreThread(tstate)获取GIL ,调用Python,并在完成后使用PyEval_SaveThread()释放它。

© www.soinside.com 2019 - 2024. All rights reserved.