线程化的Python与C绑定同步
我正在为我自己的一个实时C库实现Python绑定。我了解到,Python中的线程并不是真正的线程,它们并不能真正并行运行(因为有一个叫做全局解释器锁的东西)。不过,我还是需要考虑以下情况。
想象一下下面这个C函数:
int stoppable_lock(mutex *m, bool *stop)
{
while (!stop || !*stop)
if (timed_lock(m, SOME_TIME) == SUCCESS)
return SUCCESS;
return DIDNT_LOCK;
}
这个函数会等待一个互斥锁,但通过使用stop
,可以被取消。在最简单的情况下,无论其他使用这个互斥锁的线程发生什么(比如它们在持有锁的时候意外终止),我都希望能够优雅地停止使用这个函数的线程。
我的问题是,如何为这个函数编写一个Python的包装器。通过解析参数(使用PyArg_ParseTuple
),我可以获取到互斥锁,这没问题。然而,能传递给这个函数的参数类型似乎只有字符串、数字和对象。前两种显然不能替代bool *
,而且我怀疑为bool *
编写一个包装器是否是个好主意。
我的问题是,如何获取一个参数,它是一个变量的引用(与其他Python线程共享),而不仅仅是它的一个副本?
这是我缺失的部分的函数(用于像mutex.stoppable_lock(stop)
这样的用法):
static PyObject *_stoppable_lock(_mutex *obj, PyObject *args)
{
int ret;
BOOL_STAR stop;
if (!PyArg_ParseTuple(args, "?", &stop))
{
PyErr_SetString(PyExc_ValueError, "Usage: mutex.stoppable_lock(BOOL_STAR)");
return NULL;
}
ret = stoppable_lock(obj->orig, stop);
if (_set_exception(ret))
return NULL;
Py_RETURN_NONE;
}
在这里,我不知道BOOL_STAR
和"?"
应该是什么。
3 个回答
Python中的线程是真正的线程(至少在CPython中,对于大多数线程实现的后端来说是这样的)。不过你说得对,由于有个叫做GIL的东西,Python代码本身是不能并行运行的,尽管很多函数(特别是输入输出相关的)是可以的。
关于你的主要问题:如果你想直接跟随C语言的实现逻辑(有一个指向布尔值的引用),NPE已经说过了:你可以把它放在一个可变对象里面,比如:
class MutableBool:
value = False
否则,整个stoppable_lock
的代码也可以很容易地用Python重新实现,这样可能会更简单明了。
我的问题是,我怎么才能获取一个参数,这个参数是指向一个变量的引用(这个变量是和其他Python线程共享的),而不仅仅是它的一个副本呢?
你可以把 stop
放在一个可变对象里,然后传递这个对象的引用。
你需要为 bool
创建一个包装类型,并且要有 Python 的修改函数来处理里面的值。如果你不想自己创建一个类型,并且相信 C 语言中的 _Bool 和 C++ 的 bool 是一样的,那么你可以使用 ctypes.c_bool。这样的话,Python 脚本可以这样写:
stop = ctypes.c_bool(False)
stoppable_lock(mutex, ctypes.addressof(stop))
然后其他线程可以通过 stop.value = True
来修改这个值。扩展模块会期待一个长整型,然后把它转换成一个指针。如果你想要更安全一些的类型检查,就需要自己创建一个 bool 的包装类型。