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.
source to share
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]();
}
source to share