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

时间:2013-04-11 14:55:50

标签: python multithreading callback 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驱动程序线程中调用并运行,并且将与您的程序异步运行(不在同一个线程中)。

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

所以我想我们可以安全地假设dll正在产生一个线程。

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

1 个答案:

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