可以在自己的线程中运行的结构的属性



假设我有一个实现Decoder特性的视频解码器。很自然,我想要一个在自己的线程上运行这个解码器的方法:

fn run(&mut self) {
//These are `Arc` so they can be accessed from the thread, but what if they were not?
let on_consume = on_consume.clone();
let on_produce = on_produce.clone();
//launches the thread
self.stoppable_thread.run(
format!("ffmpeg_software_decoder_{}", self.domain.name()).as_str(),
Box::new(move |should_continue: Arc<AtomicBool>| {
while should_continue.load(Ordering::Relaxed) {

正如您所看到的,一个问题是我不能在启动的线程中引用self。因此,方法应该是这样的:

fn run(s: Arc<Mutex<dyn Decoder>>) {

通过这种方式,我可以从线程内部访问解码器的内部变量。

不过,我想要这种方法的一个特点。类似这样的东西:

use std::sync::{Arc, Mutex};
pub trait Runnable<T> {
fn run(s: Arc<Mutex<T>>) -> Result<(), ()>;
}
pub trait Decoder: Runnable<dyn Decoder> {
}

然而,这不会起作用:

Compiling playground v0.0.1 (/playground)
error[E0391]: cycle detected when computing the super predicates of `Decoder`
--> src/lib.rs:7:1
|
7 | pub trait Decoder: Runnable<dyn Decoder> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: ...which requires computing the super traits of `Decoder`...
--> src/lib.rs:7:1
|
7 | pub trait Decoder: Runnable<dyn Decoder> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: ...which again requires computing the super predicates of `Decoder`, completing the cycle
note: cycle used when collecting item types in top-level module
--> src/lib.rs:7:1
|
7 | pub trait Decoder: Runnable<dyn Decoder> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to previous error

对此,什么是好的解决方案?

对此,什么是好的解决方案?

好吧,这在一定程度上取决于你实际想做什么。

首先,如果你说:

一个可以在自己的线程中运行的结构

我宁愿想象这样的特征:

pub trait Runnable {
/// Process in a fresh thread
fn run(s: Arc<Mutex<Self>>);
}

为什么一个"可运行结构"可以运行Self以外的任何东西,所以没有必要使Runnable通用。

如果我们进一步假设你的Decoder可处理的(即,可以解码(,我想这个特征有一个这样的方法:

pub trait Decoder {
/// Process `self`, can take a while
fn process(&mut self);
}

注意,它只需要self,这使您可以轻松直接地访问实现结构的所有字段,这在您实现它时可能非常有用。当然,这不会产生新的线程,所以您可能不想调用它,但这没关系,因为我们有Runnable::run函数。

在我看来,这很好地打破了个体特征的概念。然而,你仍然可以用一种有用的方式将它们组合在一起。例如,您可以为所有Decoder创建Runnable的空白实现(这将限制Runnable在其他上下文中的可用性(,如下所示:

impl<T: Decoder + Send + 'static> Runnable for T {
fn run(s: Arc<Mutex<Self>>) {
std::thread::spawn(move || {
s.lock().unwrap().process();
});
}
}

这种空白的实现意味着,当您实现Decoder时,您只需实现process函数,并且会自动为您提供生成新线程的Runnable实现(如果是Sync'static,但除此之外,无论如何都没有使用线程的意义(。

你可以这样使用它:

#[derive(Debug)]
struct MyDecRun {
foo: u32
}
impl Decoder for MyDecRun {
fn process(&mut self) {
// Do something useful ...
self.foo += 1;
}
}
fn main() {
let a = Arc::new(Mutex::new(MyDecRun{foo: 42}));
// Process via Runnable::run that means in a new thread
MyDecRun::run(a.clone());
println!("started");
std::thread::sleep(std::time::Duration::from_secs(1));
println!("final value: {:?}", a.lock().unwrap());
std::thread::sleep(std::time::Duration::from_secs(1));
}

操场的完整示例


但是,请注意,以上假设您的解码器"原位"工作。根据我个人的经验,"解码器"通常以"制作人"的风格工作。在这种情况下,您更希望使用期货和/或渠道来传递结果,甚至传递输入(这将是不同的结构(,并具有反映生产风格的特性,如以下所示:

pub trait Decodable {
type Output;
/// Process `self`, can take a while
fn decode(self) -> Option<Self::Output>;
}

然后,如果您只是对许多Decodable的结果感兴趣,并且希望它们是并行处理的,那么您可以使用人造丝为您进行并行处理,使您的代码更加简单:操场上的完整示例。

但如果需要的话,你仍然可以自己摆弄线程和通道,我的意思是,这真的取决于你在做什么

最新更新