向量中函数的线程调用
Threaded calling of functions in a vector
我有一个 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
,它也可以工作,但那是不稳定的,我认为它只工作因为它立即连接回主线程。
更新问题
I meant "call them in their own thread"
最简单的做法是尽可能避免 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);
}
在这里,我们可以创建一个引用计数特征对象。每次生成新线程时,我们都可以克隆特征对象(增加引用计数)。每个线程都有自己的特征对象引用。
If I use thread::scoped
, it works too
thread::scoped
非常棒;真的很不幸,由于一些复杂的交互不是最好的,它需要被标记为不稳定。
作用域线程的一个好处是线程保证在特定时间结束:当JoinGuard
被删除时。这意味着作用域线程可以包含 非'static
引用,只要这些引用比线程持续时间更长!
生成的线程无法保证它们的寿命。这些线程可能存在 "forever"。他们采用的任何引用也必须存在 "forever",因此存在 'static
限制。
这可以解释你原来的问题。你有一个非 'static
生命周期的向量,但你正在将指向该向量的引用传递给线程。如果在线程退出之前释放向量,您可能会尝试访问未定义的内存,这会导致 C 或 C++ 程序崩溃。这是 Rust 帮你解决的问题!
原问题
Call functions in vector without consuming 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]();
}
我有一个 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
,它也可以工作,但那是不稳定的,我认为它只工作因为它立即连接回主线程。
更新问题
I meant "call them in their own thread"
最简单的做法是尽可能避免 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);
}
在这里,我们可以创建一个引用计数特征对象。每次生成新线程时,我们都可以克隆特征对象(增加引用计数)。每个线程都有自己的特征对象引用。
If I use
thread::scoped
, it works too
thread::scoped
非常棒;真的很不幸,由于一些复杂的交互不是最好的,它需要被标记为不稳定。
作用域线程的一个好处是线程保证在特定时间结束:当JoinGuard
被删除时。这意味着作用域线程可以包含 非'static
引用,只要这些引用比线程持续时间更长!
生成的线程无法保证它们的寿命。这些线程可能存在 "forever"。他们采用的任何引用也必须存在 "forever",因此存在 'static
限制。
这可以解释你原来的问题。你有一个非 'static
生命周期的向量,但你正在将指向该向量的引用传递给线程。如果在线程退出之前释放向量,您可能会尝试访问未定义的内存,这会导致 C 或 C++ 程序崩溃。这是 Rust 帮你解决的问题!
原问题
Call functions in vector without consuming 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]();
}