我从他的Rust FFI指南中遵循了Michael-F-Bryan的“动态加载和插件”一章,但是我将插件存储在HashMap
(HashMap<&'static str, Box<dyn Plugin>
)中,而不是在a中存储,Vec
以便可以Plugin
单独调用函数。
我希望插件使用自己的循环定义一个异步函数,该循环使用通道(std
或tokio
)与应用程序的主要部分进行通信。
async
多亏了async_trait板条箱,解决了无法在traits中使用函数这一事实很容易,但是我现在面临的问题是,我无法使用该模块生成新线程,因为新线程可能会超过PluginManager
。
我试图在没有动态模块的锈迹重现的操场上重新创建它,在这里我面临着同样的错误(注意:这不包括任何形式的通道通信)
与第一个错误不同,我无法为第二个错误重新创建一个锈蚀操场(因为它在运行时发生)。我没有使用新的线程,而是使用tokio的事件循环来处理async
函数。这在沙盒中有效,但在使用共享库作为插件时无效。在运行时,它抛出:
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
已移至正在生成的Future中,因此可能在拥有的线程之外的另一个线程上运行PluginManager
。编译器无法知道将来不会超过PluginManager
。
有许多可能的解决方案,例如:
将插件实例存储在中Arc<Box<T>>
,以便在运行时对它们进行引用计数。然后,即使插件管理器的寿命不长于您生成的期货,也没关系
使PluginManager
一个不变的静态单例
在这种情况下,我怀疑您希望PluginManager
成为单例。您可以通过用new
构造get
延迟实例并返回引用的方法来代替构造函数来做到这一点:
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::sync::Lazy
了after_cell板条箱。该功能也可在每晚的std中使用,因此最终将成为标准库的一部分。
该spawn_plugins
函数需要稍作更改以指定它需要一个静态self:
pub async fn spawn_plugins(&'static self) {
//...
最后,您的主要功能变为:
async fn main() {
let mgr = PluginManager::get();
mgr.spawn_plugins().await;
}
关于另一个问题,这是一个单独的问题-如果您一次发布一个问题,则堆栈溢出效果最好。
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句