如何在Python中并行调用dll函数

发布于 2024-11-03 02:28:26 字数 1407 浏览 1 评论 0原文

我有一个 python 程序,它使用 ctypes 来调用我向其传递指针的 dll 函数。它应该连续地将数据写入该指针,我希望我的程序循环并读取指针内容。一个粗略的模板看起来像:

from ctypes import *
import copy
lib = cdll.lib
pointer = c_char_p(" "*100) #however large a buffer I need
#thread this
lib.dostuff(pointer)
#end thread
while True:
    data = pointer.value
    print data

dostuff(),在我的具体情况下,是用 C 编写的,打开一个文件并对其进行解码,将数据作为流运行到字符数组中。

问题是我无法在 python 中使用常规线程模块,因为线程持有 GIL,因为读取 dll 被视为文件 I/O,或者因为 dll 本身执行文件 I/0。因此,循环在 dostuff() 完成之前不会运行。它阻塞的原因是什么(dll 调用总是阻塞吗?),我该如何解决这个问题?

编辑: ----------已解决------------------------ 正如下面的samplebias 所指出的,ctypes 释放了GIL 锁。我发现我的程序中的阻塞问题是我正在运行一个队列: 代码看起来有点像这样

import Queue
from threading import Thread

queue = Queue()

def runthread():
     lib.dostuff(pointer)
     while True:
        queue.put(pointer.value)

thread = Thread(target=runthread)
thread.start()
while True:
    data = queue.get()
    dostuffwithdata(data)

程序正在阻塞,因为当队列为空时,queue.get() 会阻塞,直到有东西进入!当然,由于我没有单独线程 dll 调用,因此它在我将指针结果推送到队列之前就完成了。解决方案看起来有点像这样:

import Queue
from threading import Thread

queue = Queue()

def runthread():
     q = Thread(target=lib.dostuff, args=(pointer,))
     q.start()
     while True:
         queue.put(pointer.value)

thread = Thread(target=runthread)
thread.start()
while True:
   data = queue.get()
   dostuffwithdata(data)

我希望这对某人有帮助!

I have a python program that uses ctypes to call a dll function to which I pass a pointer. It is supposed to write data continuously to that pointer and I want my program to loop and read the pointers contents. A rough template would look like:

from ctypes import *
import copy
lib = cdll.lib
pointer = c_char_p(" "*100) #however large a buffer I need
#thread this
lib.dostuff(pointer)
#end thread
while True:
    data = pointer.value
    print data

dostuff(), in my specific case, is written in C and opens a file and decodes it, running the data as a stream into a character array.

The problem is that I can't use the regular threading module in python since the thread holds the GIL either since reading the dll is considered file I/O or because the dll itself does file I/0. Therefore, the loop does not run until dostuff() is complete. What is the reason it blocks (will dll calls always block?) and how can I get around this?

EDIT:
----------SOLVED----------------------
As samplebias points out below, ctypes releases the GIL lock. I discovered that the blocking issue in my program was that I was running a queue:
The code looked a bit like this

import Queue
from threading import Thread

queue = Queue()

def runthread():
     lib.dostuff(pointer)
     while True:
        queue.put(pointer.value)

thread = Thread(target=runthread)
thread.start()
while True:
    data = queue.get()
    dostuffwithdata(data)

The program was blocking because queue.get() blocks when the queue is empty, until something goes inside! Of course, since I didn't thread the dll call alone, it finished before I pushed the pointers results to the queue. The solution looks a bit like this:

import Queue
from threading import Thread

queue = Queue()

def runthread():
     q = Thread(target=lib.dostuff, args=(pointer,))
     q.start()
     while True:
         queue.put(pointer.value)

thread = Thread(target=runthread)
thread.start()
while True:
   data = queue.get()
   dostuffwithdata(data)

I hope this helps someone!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

折戟 2024-11-10 02:28:26

这绝对可以使用线程来实现,因为 ctypes 在调用 C 函数之前释放 GIL。这使得(除其他外)C 例程能够回调到 Python 代码而不会造成死锁。

您将遇到的唯一问题是如何向 DLL 发出信号以停止传递数据,但也有一些方法可以解决这个问题,例如传递第二个指针作为指示何时返回的标志。

这是一个与您的问题相符的工作示例,例如,GIL 被发布,Python 和 C 代码同时运行:

共享对象:test.c

#include <stdint.h>
#include <stdio.h>

void
dostuff(uint64_t *ptr)
{
    while (1)
        (*ptr)++;
}

编译它:

% gcc -shared -g -o test.so test.c -fPIC

Python 代码: test.py

import ctypes
import sys
import time
import threading

lib = ctypes.cdll.LoadLibrary('./test.so')
val = ctypes.c_uint64(0)

def loop():
    lib.dostuff(ctypes.byref(val))

t1 = threading.Thread(target=loop)
t1.start()

for i in range(1000):
    sys.stdout.write('%s ' % val.value)
    sys.stdout.flush()
    time.sleep(0.05)

输出

% python test.py 
0 24664442 48388062 71628820 94834416 118004961 141095893 164936784 ... ...

This can definitely work using threading, since ctypes releases the GIL before calling a C function. This enables (among other things) the C routine to call back into Python code without creating a deadlock.

About the only problem you'll encounter is how to signal to the DLL to stop delivering data, but there are ways to solve that as well, e.g. passing a 2nd pointer as a flag indicating when to return, for example.

Here is a working example along the lines of your question, e.g. the GIL is released and the Python and C code run concurrently:

Shared object: test.c

#include <stdint.h>
#include <stdio.h>

void
dostuff(uint64_t *ptr)
{
    while (1)
        (*ptr)++;
}

Compile it:

% gcc -shared -g -o test.so test.c -fPIC

Python code: test.py

import ctypes
import sys
import time
import threading

lib = ctypes.cdll.LoadLibrary('./test.so')
val = ctypes.c_uint64(0)

def loop():
    lib.dostuff(ctypes.byref(val))

t1 = threading.Thread(target=loop)
t1.start()

for i in range(1000):
    sys.stdout.write('%s ' % val.value)
    sys.stdout.flush()
    time.sleep(0.05)

Output

% python test.py 
0 24664442 48388062 71628820 94834416 118004961 141095893 164936784 ... ...
合久必婚 2024-11-10 02:28:26

Python 的 multiprocessing 模块具有与线程类似的接口,但没有 GIL。

Python's multiprocessing module has an interface similar to threading's but with no GIL.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文