【问题标题】:Threaded calling of functions in a vector向量中函数的线程调用
【发布时间】:2015-06-07 02:05:03
【问题描述】:

我有一个EventRegistry,人们可以使用它来注册事件监听器。然后在广播事件时调用适当的侦听器。但是,当我尝试对其进行多线程处理时,它无法编译。我怎样才能让这段代码工作?

use std::collections::HashMap;
use std::thread;

struct EventRegistry<'a> {
  event_listeners: HashMap<&'a str, Vec<Box<Fn() + Sync>>>
}

impl<'a> EventRegistry<'a> {
  fn new() -> EventRegistry<'a> {
    EventRegistry {
      event_listeners: HashMap::new()
    }
  }

  fn add_event_listener(&mut self, event: &'a str, listener: Box<Fn() + Sync>) {

    match self.event_listeners.get_mut(event) {
      Some(listeners) => {
        listeners.push(listener);
        return
      },
      None => {}
    };

    let mut listeners = Vec::with_capacity(1);
    listeners.push(listener);

    self.event_listeners.insert(event, listeners);
  }

  fn broadcast_event(&mut self, event: &str) {

    match self.event_listeners.get(event) {
      Some(listeners) => {
        for listener in listeners.iter() {
          let _ = thread::spawn(|| {
            listener();
          });
        }
      }
      None => {}
    }

  }
}

fn main() {
  let mut main_registry = EventRegistry::new();

  main_registry.add_event_listener("player_move", Box::new(|| {
    println!("Hey, look, the player moved!");
  }));

  main_registry.broadcast_event("player_move");
}

Playpen(不确定是否最小,但会产生错误)

如果我使用thread::scoped,它也可以工作,但这是不稳定的,我认为它只是因为它立即加入主线程才有效。

【问题讨论】:

  • Edited =) 不确定它是否足够好。
  • 请注意,Rust 样式是 4 空格缩进。
  • 请发布实际错误。

标签: multithreading function rust borrow-checker


【解决方案1】:

更新问题

我的意思是“在他们自己的线程中调用他们”

如果可能,最简单的做法是避免使用 Fn* 特征。如果你知道你只是在使用完整的功能,那么很简单:

use std::thread;

fn a() { println!("a"); }
fn b() { println!("b"); }

fn main() {
    let fns = vec![a as fn(), b as fn()];

    for &f in &fns {
        thread::spawn(move || f());
    }

    thread::sleep_ms(500);
}

如果你因为某种原因不能使用它(比如你想接受闭包),那么你需要更明确一点并使用Arc

use std::thread;
use std::sync::Arc;

fn a() { println!("a"); }
fn b() { println!("b"); }

fn main() {
    let fns = vec![
        Arc::new(Box::new(a) as Box<Fn() + Send + Sync>),
        Arc::new(Box::new(b) as Box<Fn() + Send + Sync>),
    ];

    for f in &fns {
        let my_f = f.clone();
        thread::spawn(move || my_f());
    }

    thread::sleep_ms(500);
}

在这里,我们可以创建一个引用计数的特征对象。每次生成新线程时,我们都可以克隆 trait 对象(增加引用计数)。每个线程都有自己对 trait 对象的引用。

如果我使用thread::scoped,它也可以工作

thread::scoped 非常棒;很遗憾,由于一些不是最好的复杂交互,它需要被标记为不稳定。

作用域线程的好处之一是线程保证在特定时间结束:当JoinGuard 被删除时。这意味着作用域线程可以包含'static 引用,只要这些引用的持续时间比线程长!

生成的线程无法保证它们的寿命;这些线程可能会“永远”存在。他们采用的任何引用也必须“永远”存在,因此受到 'static 限制。

这有助于解释您的原始问题。您有一个具有非'static 生命周期的向量,但是您将指向该向量的引用传递给线程。如果要在线程退出之前释放向量,您可能会尝试访问未定义的内存,这会导致 C 或 C++ 程序崩溃。这是 Rust 帮助你!

原问题

在向量中调用函数而不使用它们

答案是你直接打电话给他们:

fn a() { println!("a"); }
fn b() { println!("b"); }

fn main() {
    let fns = vec![Box::new(a) as Box<Fn()>, Box::new(b) as Box<Fn()>];
    fns[0]();
    fns[1]();
    fns[0]();
    fns[1]();
}

Playpen

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-08-17
    • 2020-09-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多