我想从扭曲的应用程序中使用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驱动程序线程中调用并运行,并且将与您的程序异步运行(不在同一个线程中)。
可以找到完整的文档here。为简单起见,我在示例中更改了函数签名。
所以我想我们可以安全地假设dll正在产生一个线程。
我所拥有的锁是否确保回调函数在主循环中l
操作的中间时不会尝试访问列表pop
,反之亦然?或者,只有在使用使用threading
库创建的线程时,此方案才有效吗?这里的推荐做法是什么?
答案 0 :(得分:2)
ctypes _CallPythonObject
做的第一件事就是调用PyGILState_Ensure()
,如果需要,会调用PyThreadState_New
来创建一个新的线程状态。除此之外,它是vanilla 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之外创建的线程获得一个“虚拟”名称。
<强> 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: %d\n", 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;
}