有没有办法从python调用rust的异步接口?

有没有办法从python调用rust的异步接口?,python,asynchronous,rust,python-cffi,Python,Asynchronous,Rust,Python Cffi,我将rust的reqwest的一些函数包装到req.lib文件中,并使用cffi从python成功调用它。然而,reqwest::blocking::Client迫使我在python中使用多线程。我发现在rust中可以在异步模式下调用reqwest。我想知道有没有办法使req.libasync?即使是半异步对我来说也是可以的 例如,当前存根签名为: #[no_mangle] pub extern "C" fn urlopen(url: *const c_char) -> *mut c_ch

我将rust的
reqwest
的一些函数包装到
req.lib
文件中,并使用
cffi
从python成功调用它。然而,
reqwest::blocking::Client
迫使我在python中使用多线程。我发现在rust中可以在异步模式下调用
reqwest
。我想知道有没有办法使
req.lib
async?即使是半异步对我来说也是可以的

例如,当前存根签名为:

#[no_mangle]
pub extern "C" fn urlopen(url: *const c_char) -> *mut c_char
我可以写一些类似于:

#[no_mangle]
pub extern "C" fn urlopen(url: *const c_char) -> u64  // return request unique id

#[no_mangle]
pub extern "C" fn is_finished(req_id: u64) -> bool  // whether given request is done

#[no_mangle]
pub extern "C" fn fetch_result(req_id: u64) -> *mut c_char  // fetch response

因此,
cffi
调用不再锁定主线程。我可以使用单个线程来调用多个请求。欢迎提供任何建议或最佳实践。

异步代码通过特殊的运行时执行,对于python和rust,它们是不同的且不兼容的库。在这里,您不能简单地在不同语言之间共享未来,它必须在创建它的同一语言中运行

就您的示例而言,这意味着您需要在rust executor(例如tokio)中运行
客户端
,然后从中获得反馈。最简单的方法是创建一个全局的:

使用lazy\u static::lazy\u static;
使用tokio::runtime::runtime;
懒惰的人!{
static ref RUNTIME:RUNTIME=RUNTIME::new().unwrap();
}
然后在生成之后,您需要有一个反馈,因此您可以使用两个带有状态和结果的映射:

使用std::collections::HashMap;
使用std::sync::RwLock;
使用期货::前奏::*;
使用tokio::sync::oneshot;
类型FutureId=u64;
输入UrlResult=reqwest::Result;
类型SyncMap=RwLock;
懒惰的人!{
//反馈通道的映射。计算结果后,将其存储在“结果”中`
静态引用状态:SyncMap=SyncMap::default();
//缓存结果存储
静态引用结果:SyncMap=SyncMap::default();
}
fn gen_unique_id()->u64{..}
#[没有损坏]
pub extern“C”fn urlopen(url:*const C_char)->FutureId{
让url:&str=/*转换url*/;
let(tx,rx)=单点::通道();
生成(异步移动){
让body=reqwest::get(url)。然后(|b | b.text())。等待;
tx.send(body).unwrap();//bool{
//首次签入缓存
如果RESULTS.read().unwrap()包含密钥(&req\u id){
真的
}否则{
让mut res=RESULTS.write().unwrap();
让mut statuses=statuses.write().unwrap();
//如果缓存中没有任何内容,请检查反馈通道
如果让一些(rx)=状态。获取信息(&req)id{
让val=匹配rx。尝试{
Ok(val)=>val,
错误(41;)=>{
//在这里以某种方式处理错误
返回true;
}
};
//并缓存结果(如果可用)
res.insert(请求id,val);
真的
}否则{
//未知请求id
真的
}
}
}
那么,获取结果相当简单:

#[无损坏]
发布外部“C”fn获取结果(请求id:u64)->*常量C\u字符{
设res=RESULTS.read().unwrap();
res.get(&req\u id)
//可能应该以更好的方式处理“ok()”
.然后(| val | val.as_ref().ok())
.map(| val | val.as_ptr())
.unwrap_或(std::ptr::null())作为*const_
}
链接

请记住,上述解决方案有其优点:

  • 缓存结果,可多次取数
  • API(希望)是线程安全的
  • 读写锁是分开的,这可能是比互斥锁更快的解决方案
还有一些重要的缺点:

  • 结果
    不断增长且从不清除
  • 线程安全使事情变得有点复杂,所以可能不需要,可以用于全局,而不是锁
  • 缺乏正确的错误处理
  • 使用RwLock,它有时可能比其他一些原语表现得更糟糕
  • status
    at
    已完成
    acquire write access,不过最好先有一个read access

异步代码是通过特殊的运行时执行的,对于python和rust来说,它们是不同且不兼容的库。在这些库中,您不能简单地在不同语言之间共享未来,它必须在创建它的地方以相同的语言运行

就您的示例而言,这意味着您需要在rust executor(例如tokio)中运行
客户端
,然后从中获得反馈。最简单的方法是创建一个全局客户端:

使用lazy\u static::lazy\u static;
使用tokio::runtime::runtime;
懒惰的人{
static ref RUNTIME:RUNTIME=RUNTIME::new().unwrap();
}
然后在生成之后,您需要有一个反馈,因此您可以使用两个带有状态和结果的映射:

使用std::collections::HashMap;
使用std::sync::RwLock;
使用期货::前奏::*;
使用tokio::sync::oneshot;
类型FutureId=u64;
输入UrlResult=reqwest::Result;
类型SyncMap=RwLock;
懒惰的人{
//反馈通道的映射。计算结果后,将其存储在“结果”中`
静态引用状态:SyncMap=SyncMap::default();
//缓存结果存储
静态引用结果:SyncMap=SyncMap::default();
}
fn gen_unique_id()->u64{..}
#[没有损坏]
pub extern“C”fn urlopen(url:*const C_char)->FutureId{
让url:&str=/*转换url*/;
let(tx,rx)=单点::通道();
生成(异步移动){
让body=reqwest::get(url)。然后(|b | b.text())。等待;
tx.send(body).unwrap();//bool{
//首次签入缓存
如果RESULTS.read().unwrap()包含密钥(&req\u id){
真的
}否则{
让mut res=RESULTS.write().unwrap();
让mut statuses=statuses.write().unwrap();
//如果缓存中没有任何内容,请检查反馈通道
如果让一些(rx)=状态。获取信息(&req)id{
让val=匹配rx。尝试{
Ok(val)=>val,
错误(