Threaded function call in vector

I have EventRegistry

one that people can use to register event listeners. It then calls the appropriate listeners when broadcasting the event. But, when I try to use multithreading, it doesn't compile. How can I get this code to work?

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");
}

      

Dummy (not sure if it's minimal, but it throws an error)

If I use thread::scoped

it works too, but this one is unstable and I think it only works because it immediately attaches to the main thread.

+3


source to share


1 answer


Updated question

I meant "call them in my stream"

The simplest thing is to avoid traits Fn*

if possible. If you know you are only using full functions, then this is simply:

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);
}

      

If you cannot use this for some reason (like you want to accept closure), you need to be more explicit and use 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);
}

      

Here we can create an object with reference counting. We can clone the traits object (increasing the number of links) every time we create a new stream. Each thread gets its own reference to the object object.

If I use thread::scoped

it works too

thread::scoped

is pretty awesome; he really regrets that he should be marked as erratic due to some complex interactions that weren't the best.



One of the advantages of a cloud stream is that the stream is guaranteed to end at a certain time: when dropped JoinGuard

. This means that allowed streams are allowed to contain no links 'static

if those links last longer than the stream!

The spawned thread has no such guarantees as to how long they live; these streams can live "forever". Any links they take must also live "forever", thus limiting 'static

.

This explains your original problem. You have a vector with no lifespan 'static

, but you are passing references pointing to that vector to the stream. If the vector needs to be freed before the stream exits, you might try to access undefined memory, which will crash your C or C ++ programs. This Rust helps you!

Original question

Call functions in a vector without consuming them

The answer is that you just call them:

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]();
}

      

Dummy

+3


source







All Articles