python - 如何将(快速更新的)变量从 ctypes C 代码传递给 Python?通过引用传递?

标签 python c dll ctypes

我正在尝试开发将被编译为 DLL(或 Linux 的 .SO)的 C 代码,目的是高速处理一些外部输入并将结果返回给 Python 以进行后续处理。

我的问题是:从用于 Python 的 C 函数定期返回值(每秒 >1000 秒)的最佳方法是什么?

我创建了一个测试用例如下:

//dummy_function.c
#include <stdio.h>
#include <Windows.h>

__declspec(dllexport) int runme() {
    int i;
    for (i=1; i<= 500; i++) {
        printf("int = %d \n", i);
        Sleep(100);  // Add some arbitrary delay for now
    }
}

注意,我导入 Windows.h 以使用虚拟时间延迟(模拟我的真实问题)。此代码可以使用 unistd.h 在 unix 上运行(根据:What is the proper #include for the function 'sleep' in C?)

然后这段代码被编译成.dll

gcc -shared -o dummy_function.dll dummy_function.c

并通过以下方式在 Python 中导入:

import ctypes
libc = ctypes.CDLL('dummy_function.dll')
libc.runme()  # Start running

执行此 Python 代码时,它会打印出递增的整数。但是,使用 Python 从 C 中捕获打印输出然后对其进行处理似乎不是执行此操作的好方法(/不可扩展以实现高速)。

相反,我想知道是否有一种方法可以更轻松地将变量从 DLL 传递给 Python。我想我不想在 C 中使用 return 函数,因为这会退出函数。

我读过有关在 C 中通过引用传递的内容,因此想知道这是否与 ctypes 兼容。我可以定义一些 C 代码写入的内存位置,然后 Python 对此进行轮询吗?或者更好的是,某种形式的队列/缓冲区以避免丢失事件?

任何建议将不胜感激,谢谢!

最佳答案

虽然您熟悉这些概念,但这里是 [Python 3.Docs]: ctypes - A foreign function library for Python .

Python 代码在 C 代码运行时轮询内存区域,意味着不止一个线程。
这是一个例子。

dll.c:

#include <stdio.h>
#include <Windows.h>

#if defined(_WIN32)
#  define DLL_EXPORT __declspec(dllexport)
#else
#  define DLL_EXPORT
#endif

#define PRINT_MSG_2XI(ARG0, ARG1) printf("From C - [%s] (%d) - [%s]:  ARG0: 0x%016p, ARG1: %d\n", __FILE__, __LINE__, __FUNCTION__, ARG0, ARG1)


typedef struct Srtruct_ {
    int value;
    int sentinel;
} Struct;


DLL_EXPORT int func0(Struct *ptr) {
    int counter = 0;
    if (!ptr) {
        return -1;
    }
    while (ptr->sentinel) {
        ptr->value++;
        counter++;
        PRINT_MSG_2XI(ptr, ptr->value);
        Sleep(200);
    }
    return counter;
}

代码.py:

#!/usr/bin/env python3

import sys
import ctypes
import time
import threading


DLL_NAME = "./dll.dll"


class Struct(ctypes.Structure):
    _fields_ = [
        ("value", ctypes.c_int),
        ("sentinel", ctypes.c_int),
    ]


def thread_func(func, arg0):
    ret = func(arg0)
    print("\nFrom Python - Function returned {:d}".format(ret))


def main():
    dll = ctypes.CDLL(DLL_NAME)
    func0 = dll.func0
    func0.argtypes = [ctypes.POINTER(Struct)]
    func0.restype = ctypes.c_int

    data = Struct(30, 1)
    t = threading.Thread(target=thread_func, args=(func0, data,))
    t.start()
    time.sleep(1)
    print("From Python - Monitored value: {:d}".format(data.value))
    time.sleep(1)
    print("From Python - Monitored value: {:d}".format(data.value))
    data.sentinel = 0
    time.sleep(0.5)


if __name__ == "__main__":
    print("Python {:s} on {:s}\n".format(sys.version, sys.platform))
    main()
    print("\nDone.")

输出:

[cfati@CFATI-5510-0:e:\Work\Dev\StackOverflow\q056197585]> sopr.bat
*** Set shorter prompt to better fit when pasted in StackOverflow (or other) pages ***

[prompt]> "c:\Install\x86\Microsoft\Visual Studio Community\2017\VC\Auxiliary\Build\vcvarsall.bat" x64
**********************************************************************
** Visual Studio 2017 Developer Command Prompt v15.9.11
** Copyright (c) 2017 Microsoft Corporation
**********************************************************************
[vcvarsall.bat] Environment initialized for: 'x64'

[prompt]> dir /b
code.py
dll.c

[prompt]> cl /nologo /DDLL dll.c  /link /NOLOGO /DLL /OUT:dll.dll
dll.c
   Creating library dll.lib and object dll.exp

[prompt]> dir /b
code.py
dll.c
dll.dll
dll.exp
dll.lib
dll.obj

[prompt]> "e:\Work\Dev\VEnvs\py_064_03.07.03_test0\Scripts\python.exe" code.py
Python 3.7.3 (v3.7.3:ef4ec6ed12, Mar 25 2019, 22:22:05) [MSC v.1916 64 bit (AMD64)] on win32

From C - [dll.c] (27) - [func0]:  ARG0: 0x00000152EB84CE90, ARG1: 31
From C - [dll.c] (27) - [func0]:  ARG0: 0x00000152EB84CE90, ARG1: 32
From C - [dll.c] (27) - [func0]:  ARG0: 0x00000152EB84CE90, ARG1: 33
From C - [dll.c] (27) - [func0]:  ARG0: 0x00000152EB84CE90, ARG1: 34
From C - [dll.c] (27) - [func0]:  ARG0: 0x00000152EB84CE90, ARG1: 35
From Python - Monitored value: 35
From C - [dll.c] (27) - [func0]:  ARG0: 0x00000152EB84CE90, ARG1: 36
From C - [dll.c] (27) - [func0]:  ARG0: 0x00000152EB84CE90, ARG1: 37
From C - [dll.c] (27) - [func0]:  ARG0: 0x00000152EB84CE90, ARG1: 38
From C - [dll.c] (27) - [func0]:  ARG0: 0x00000152EB84CE90, ARG1: 39
From C - [dll.c] (27) - [func0]:  ARG0: 0x00000152EB84CE90, ARG1: 40
From Python - Monitored value: 40

From Python - Function returned 10

Done.

关于python - 如何将(快速更新的)变量从 ctypes C 代码传递给 Python?通过引用传递?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56197585/

相关文章:

python - 使用其他爬取信息的 scrapy 图像管道文件名

python - pandas read_csv 列标题中带有井号

c++ - 为什么在宏中使用看似毫无意义的 do-while 和 if-else 语句?

c - 将版本信息写入注释 block

c - 如何在网络程序中监视/关闭待处理的网络 session

python - 使用 ctypes (python) 在带括号的路径中加载 dll 时出错

python - OpenCV 的 `getTextSize` 和 `putText` 返回错误的大小和像素较低的字母

python - 对新文档进行分类 - 随机森林、词袋

C# 相当于 C (WinAPI) 中的 DllMain

python - 在python中从dll调用函数