线程化的Python与C绑定同步

1 投票
3 回答
649 浏览
提问于 2025-04-17 14:12

我正在为我自己的一个实时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 个回答

0

Python中的线程是真正的线程(至少在CPython中,对于大多数线程实现的后端来说是这样的)。不过你说得对,由于有个叫做GIL的东西,Python代码本身是不能并行运行的,尽管很多函数(特别是输入输出相关的)是可以的。

关于你的主要问题:如果你想直接跟随C语言的实现逻辑(有一个指向布尔值的引用),NPE已经说过了:你可以把它放在一个可变对象里面,比如:

class MutableBool:
    value = False

否则,整个stoppable_lock的代码也可以很容易地用Python重新实现,这样可能会更简单明了。

0

我的问题是,我怎么才能获取一个参数,这个参数是指向一个变量的引用(这个变量是和其他Python线程共享的),而不仅仅是它的一个副本呢?

你可以把 stop 放在一个可变对象里,然后传递这个对象的引用。

1

你需要为 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 的包装类型。

撰写回答