在ctypes回调函数中使用线程锁



我想从扭曲的应用程序中使用ctypes dll。这里的最小示例:

from ctypes import *
from threading import Lock
lock = Lock()
dll = windll.LoadLibrary('mydll.dll')
l = [1,2,3]
def callback():
    lock.acquire()
    l.pop()
    lock.release()
    return 0
C_CALLBACK = CFUNCTYPE(c_int)
c_callback = C_CALLBACK(callback)
# this is a non blocking function call starts a hardware task that fires a callback upon completion
dll.registerCallback(c_callback)
while(True):
    # in reality this block gets called from a twisted server application
    lock.acquire()
    l.append(l.pop() + 1)
    lock.release()

dll有一个函数(dll.registerCallback),它接受一个ctypes回调函数,启动一个硬件事件,并在硬件指示硬件任务完成时触发一个回调。

来自API文档:

回调函数在DAQmx线程中调用。

网上有人试图解释什么是"DAQmx线程":

…您的回调将被调用并在DAQmx驱动程序线程中运行,并且将与您的程序异步运行(不在同一线程中)。

完整的文档可以在这里找到。为了简单起见,我更改了示例中的函数签名。

所以我们可以放心地假设dll正在生成一个线程。

锁是否可以确保回调函数在主循环中的pop操作中间时不会尝试访问列表l,反之亦然?或者这个方案只在使用threading库创建的线程时才有效?这里推荐的做法是什么?

ctypes _CallPythonObject所做的第一件事是调用PyGILState_Ensure(),它将在必要时调用PyThreadState_New来创建一个新的线程状态。除此之外,它是普通的Python线程代码,所以你的锁应该工作得很好。它在下面的例子(Linux, Python 2.7.3)中为我工作:

from ctypes import *
import threading
lock = threading.Lock()
lib = CDLL('./tmp.so')
callback_t = CFUNCTYPE(c_int)
def f():
  with lock:
    print threading.current_thread()
  return 21
callback = callback_t(f)
lib.registerCallback(callback)
>>> lock.acquire()
True
>>> t = threading.Thread(target=lib.event)
>>> t.start()
>>> lock.locked()
True
>>> lock.release()
>>> <_DummyThread(Dummy-2, started daemon -1230402704)>
res: 21

DummyThread输出是在回调中打印当前线程。在Python之外创建的线程获得一个'dummy'名称。

tmp.c:

#include <stdio.h>
#include <pthread.h>
typedef int (*callback_t)(void);
callback_t callback = NULL;
void *doCallback(void *arg)
{
    int res = callback();
    printf("res: %dn", res);
    pthread_exit(0);
}
int event(void)
{
    pthread_t callback_thread;
    pthread_create(&callback_thread, NULL, doCallback, NULL);
    pthread_join(callback_thread, NULL);
    return 0;
}
int registerCallback(callback_t foo)
{
    callback = foo;
    return 0;
}

最新更新