2013-04-11 69 views
3

我想从扭曲的应用程序中使用ctypes dll。小例子,这里炮制:在ctypes回调函数中使用线程锁

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正在产生一个线程。

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

+0

我不能肯定,都不能正常工作。我不确定有什么东西会初始化这个线程,以便它可以与Python一起工作。 – 2013-04-11 17:02:00

+0

@erksun好的,很好!谢谢。 – 2013-04-11 19:41:45

+0

我认为eryksun的评论回答了这个问题。你的锁已经足够了。 – 2013-04-11 19:45:55

回答

2

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之外创建的线程会得到一个“虚拟”名称。

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; 
}