3

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しても動作しますが、これは不安定で、すぐにメイン スレッドに参加するためだけに動作すると思います。

4

1 に答える 1

3

更新された質問

私は「自分のスレッドでそれらを呼び出す」ことを意味しました

最も簡単な方法は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 オブジェクトを作成できます。新しいスレッドを生成するたびに、特性オブジェクトを複製できます (参照カウントを増やします)。各スレッドは、特性オブジェクトへの独自の参照を取得します。

を使用するthread::scopedと、それも機能します

thread::scopedかなり素晴らしいです。複雑な相互作用が最適ではなかったために、不安定であるとマークする必要があったのは本当に残念です。

スコープ スレッドの利点の 1 つは、スレッドが特定の時間までに終了することが保証さ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]();
}

ベビーサークル

于 2015-06-07T02:14:51.513 に答える