You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将Python异步函数作为C回调函数使用(基于ctypes)

问题

我需要调用一个C库,让它调用Python函数处理IO操作,以此避免编写对应的C IO实现。现有Python代码大量使用asyncio,所有IO操作均通过队列完成,且通过ctypes加载该C库。但问题在于无法在C回调中使用await语法,请问是否有办法将Python异步函数作为C回调函数使用?

除了示例中类似non_async_callback的同步轮询方案外,是否存在通过ctypes在C回调中实现await的方式?


Python 示例代码

import asyncio
import time
import ctypes

DLL_PATH="./bin/test.dll"

dll = ctypes.CDLL(DLL_PATH)
incoming_queue=asyncio.Queue()
outgoing_queue=asyncio.Queue()

c_func =  ctypes.CFUNCTYPE(ctypes.c_uint8, ctypes.c_uint8)

async def python_callback(test_num):
    await outgoing_queue.put(test_num)
    test_num = await incoming_queue.get()
    return test_num

def non_async_callback(test_num):
    while True:
        try:
            outgoing_queue.put_nowait(test_num)
            break
        except asyncio.QueueFull:
            time.sleep(0.1)
            continue
   
    while True:
        try:
            test_num = incoming_queue.get_nowait()
            break 
        except asyncio.QueueEmpty:
            time.sleep(0.1)
            continue

    return test_num

# 初始化阶段调用
def setup_callback():
    dll.setup_callback(c_func(python_callback))

C 示例代码

#include <stdint.h>

typedef uint8_t (*CALLBACK)(uint8_t);
CALLBACK py_callback;

void setup_callback(void* callback)
{
    py_callback = (CALLBACK)callback;
}

// 由C代码其他逻辑触发调用
uint8_t python_callback(uint8_t value)
{
    uint8_t result = py_callback(value);
    // 处理返回结果
    return result;
}

可行方案分析

直接在C回调中使用await是不可能的,因为C回调是同步阻塞调用,而await属于Python异步语法,只能在async函数内使用,且依赖asyncio的事件循环调度。不过可以通过以下方式实现异步逻辑的适配,避免低效轮询:

方案1:用事件循环同步接口调度异步任务

在同步回调中,获取当前运行的asyncio事件循环,通过run_until_complete()直接运行异步回调函数,等待其完成后返回结果:

def callback_adapter(test_num):
    loop = asyncio.get_running_loop()
    # 阻塞等待异步任务执行完成
    result = loop.run_until_complete(python_callback(test_num))
    return result

# 注册适配器作为C回调
def setup_callback():
    dll.setup_callback(c_func(callback_adapter))

注意:如果C回调是在非事件循环线程中被调用,需要改用线程安全的方式调度任务并同步获取结果:

def thread_safe_callback_adapter(test_num):
    loop = asyncio.get_running_loop()
    # 线程安全地提交异步任务到事件循环
    future = asyncio.run_coroutine_threadsafe(python_callback(test_num), loop)
    # 阻塞等待任务完成并获取结果
    return future.result()

方案2:异步触发式回调(若C库支持)

如果C库支持异步回调模式(即调用回调后立即返回,不等待结果,待IO完成后再通知Python),可以将异步逻辑完全交由asyncio调度,C代码仅负责触发事件而非等待结果。但如果C库必须同步等待返回值,方案1更直接。

方案3:用asyncio.to_thread适配(Python 3.9+)

将同步回调中的逻辑放到独立线程执行,避免阻塞主事件循环:

async def async_handler(test_num):
    return await python_callback(test_num)

def callback_adapter(test_num):
    result = asyncio.run(asyncio.to_thread(async_handler, test_num))
    return result

关键注意点
  • 线程安全:若C代码在独立线程中调用回调,必须确保asyncio事件循环已启动,且使用线程安全的接口调度异步任务。
  • 性能对比:轮询方案会浪费CPU资源且响应延迟高,通过事件循环调度异步任务的方式能更高效地利用系统资源。

内容的提问来源于stack exchange,提问作者Jameaney

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 02:22:37