在新线程中从共享库执行异步函数 (Rust)
Execute asyncronus function from shared library in a new thread (Rust)
我正在关注 Michael-F-Bryan 的 Rust FFI 指南中的 Dynamic Loading & Plugins 章节,但我将插件存储在 HashMap
(HashMap<&'static str, Box<dyn Plugin>
) 而不是 Vec
这样我就可以单独调用 Plugin
的函数了。
我希望插件定义一个异步函数,它有自己的循环,使用通道 (std
or tokio
) 与应用程序的主要部分进行通信。
由于 async_trait 板条箱,解决 traits 中不能有 async
函数的问题很容易,但我现在面临的问题是,我无法生成一个使用模块的新线程,因为新线程可能比 PluginManager
.
寿命更长
我试图在没有动态模块的 rust playground 中重新创建它,但我遇到了同样的错误here(注意:这不包括任何类型的通道通信)
与第一个错误不同,我无法为第二个错误重新创建一个 Rust 游乐场(因为它发生在运行时)。我没有生成新线程,而是使用 tokio 的事件循环来处理 async
函数。这在 sandbox 中有效,但在使用共享库作为插件时无效。在运行时它抛出:
thread '<unnamed>' panicked at 'there is no timer running, must be called from the context of Tokio runtime', /home/admin/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-1.0.1/src/time/driver/handle.rs:50:18
如果有人知道解决这个问题的方法,或者甚至有同样的问题,如果你能与我分享它们就太好了,因为我已经尝试了很多但没有对我有利。
您已经认识到这个问题:对 PluginManager
拥有的对象的引用正在被移动到正在生成的未来,因此可能 运行 在另一个线程而不是拥有 PluginManager
的人。编译器无法知道未来不会超过 PluginManager
.
对此有多种可能的解决方案,例如:
将插件实例存储在 Arc<Box<T>>
中,以便在 运行 时间内对它们进行引用计数。那么即使插件管理器的寿命没有你生成的 futures 长,也没关系
使PluginManager
成为不可变的静态单例
在这种情况下,我怀疑您希望 PluginManager
成为单例。您可以通过将 new
构造函数替换为延迟构造实例的 get
方法和 returns 对它的引用来实现:
use once_cell::sync::Lazy;
impl PluginManager {
pub fn get() -> &'static Self {
static PLUGINMANAGER: Lazy<PluginManager> = Lazy::new(|| {
let mut mgr = PluginManager {
plugins: HashMap::new()
};
mgr.load_plugins();
mgr
});
&PLUGINMANAGER
}
// ...
}
请注意,此惰性构造函数会完全初始化静态实例,包括加载插件。
它使用了 once_cell crate. This functionality is also available in nightly std 中的 once_cell::sync::Lazy
,所以它最终会成为标准库的一部分。
spawn_plugins
函数需要稍微修改以指定它需要静态自身:
pub async fn spawn_plugins(&'static self) {
//...
最后你的主要功能变成了:
async fn main() {
let mgr = PluginManager::get();
mgr.spawn_plugins().await;
}
关于您的其他问题,这是一个单独的问题 - 如果您一次 post 解决一个问题,则堆栈溢出效果最好。
我正在关注 Michael-F-Bryan 的 Rust FFI 指南中的 Dynamic Loading & Plugins 章节,但我将插件存储在 HashMap
(HashMap<&'static str, Box<dyn Plugin>
) 而不是 Vec
这样我就可以单独调用 Plugin
的函数了。
我希望插件定义一个异步函数,它有自己的循环,使用通道 (std
or tokio
) 与应用程序的主要部分进行通信。
由于 async_trait 板条箱,解决 traits 中不能有 async
函数的问题很容易,但我现在面临的问题是,我无法生成一个使用模块的新线程,因为新线程可能比 PluginManager
.
我试图在没有动态模块的 rust playground 中重新创建它,但我遇到了同样的错误here(注意:这不包括任何类型的通道通信)
与第一个错误不同,我无法为第二个错误重新创建一个 Rust 游乐场(因为它发生在运行时)。我没有生成新线程,而是使用 tokio 的事件循环来处理 async
函数。这在 sandbox 中有效,但在使用共享库作为插件时无效。在运行时它抛出:
thread '<unnamed>' panicked at 'there is no timer running, must be called from the context of Tokio runtime', /home/admin/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-1.0.1/src/time/driver/handle.rs:50:18
如果有人知道解决这个问题的方法,或者甚至有同样的问题,如果你能与我分享它们就太好了,因为我已经尝试了很多但没有对我有利。
您已经认识到这个问题:对 PluginManager
拥有的对象的引用正在被移动到正在生成的未来,因此可能 运行 在另一个线程而不是拥有 PluginManager
的人。编译器无法知道未来不会超过 PluginManager
.
对此有多种可能的解决方案,例如:
将插件实例存储在
Arc<Box<T>>
中,以便在 运行 时间内对它们进行引用计数。那么即使插件管理器的寿命没有你生成的 futures 长,也没关系使
PluginManager
成为不可变的静态单例
在这种情况下,我怀疑您希望 PluginManager
成为单例。您可以通过将 new
构造函数替换为延迟构造实例的 get
方法和 returns 对它的引用来实现:
use once_cell::sync::Lazy;
impl PluginManager {
pub fn get() -> &'static Self {
static PLUGINMANAGER: Lazy<PluginManager> = Lazy::new(|| {
let mut mgr = PluginManager {
plugins: HashMap::new()
};
mgr.load_plugins();
mgr
});
&PLUGINMANAGER
}
// ...
}
请注意,此惰性构造函数会完全初始化静态实例,包括加载插件。
它使用了 once_cell crate. This functionality is also available in nightly std 中的 once_cell::sync::Lazy
,所以它最终会成为标准库的一部分。
spawn_plugins
函数需要稍微修改以指定它需要静态自身:
pub async fn spawn_plugins(&'static self) {
//...
最后你的主要功能变成了:
async fn main() {
let mgr = PluginManager::get();
mgr.spawn_plugins().await;
}
关于您的其他问题,这是一个单独的问题 - 如果您一次 post 解决一个问题,则堆栈溢出效果最好。