python - 在 CPython C 扩展类型上的属性读/写之前获取锁
问题描述
我正在为 CPython 编写一个多线程 C 扩展。我有 POD,它使用简单的libuv rwlock使线程安全。我想包装它,以便可以分配它并通过简单PyMemberDef
的从 Python 访问数据。我的问题是,仅仅获取/释放锁定就足够了getattro
,setattro
还是我错过了什么?我是 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,
};
解决方案
万一其他人尝试这样做:是的,这对您的 C 代码来说是线程安全的。
The un-asked question here is what about the Python interpreter? If only the main thread (the interpreter that called into your extension) ever calls back into/returns to the interpreter, then you don't have anything to worry about.
If however threads spawned in your C extension have the ability to call into the interpreter, you need to consider the GIL. First release the GIL and acquire the main thread's state with PyEval_SaveThread()
. Then use the interp *
stored in the thread state to spin up new thread states with PyThreadState_New(interp)
, one for each thread you intend to create in C. Finally, before calling back into the Python interpreter, acquire the GIL with PyEval_RestoreThread(tstate)
, call the Python, and release it with PyEval_SaveThread()
when you're done.
推荐阅读
- python - 根据网站 django 上的操作显示用户上个月的进度
- c# - 在 Linux 上可靠地获取捆绑的可执行文件的位置
- flutter - 如何覆盖等待和异步的抽象类?
- python - 需要上传子目录及其内容,而不仅仅是当前目录中的文件
- c# - 为什么通过 postman Kestrel 环境连接时 API 返回 404?
- ionic-framework - 如何在构建ionic2之前为android动态更改包名称
- ansible - 将 ansible 角色的任务拆分为多个文件
- javascript - 如何使用反应钩子验证表单?
- javascript - 命名为“名称”的 Javascript 数组变量在存储字符串时显示意外结果
- math - 如何从确定圆是否与线碰撞的函数中提取角度?