Reputation: 8096
I know there are hundreds of questions just like this one, but i'm having trouble wrapping my head around how to do the thing I'm trying to do.
I want an http server that accepts and processes events. On receiving/processing an event, i want the EventManager
to send an update to an ApplicationMonitor
that is tracking how many events have been accepted/processed. The ApplicationMonitor
would also (eventually) handle things like tracking number of concurrent connections, but in this example I just want my EventManager
to send an Inc('event_accepted')
update to my ApplicationMonitor
.
To be useful, I need the ApplicationMonitor
to be able to return a snapshot of the stats when the requested through a /stats
route.
So I have an ApplicationMonitor
which spawns a thread and listens on a channel for incoming Stat
events. When it receives a Stat
event it updates the stats HashMap. The stats hashmap must be mutable within both ApplicationMonitor
as well as the spawned thread.
use std::sync::mpsc;
use std::sync::mpsc::Sender;
use std::thread;
use std::thread::JoinHandle;
use std::collections::HashMap;
pub enum Stat {
Inc(&'static str),
Dec(&'static str),
Set(&'static str, i32)
}
pub struct ApplicationMonitor {
pub tx: Sender<Stat>,
pub join_handle: JoinHandle<()>
}
impl ApplicationMonitor {
pub fn new() -> ApplicationMonitor {
let (tx, rx) = mpsc::channel::<Stat>();
let mut stats: HashMap<&'static str, i32> = HashMap::new();
let join_handle = thread::spawn(move || {
for stat in rx.recv() {
match stat {
Stat::Inc(nm) => {
let current_val = stats.entry(nm).or_insert(0);
stats.insert(nm, *current_val + 1);
},
Stat::Dec(nm) => {
let current_val = stats.entry(nm).or_insert(0);
stats.insert(nm, *current_val - 1);
},
Stat::Set(nm, val) => {
stats.insert(nm, val);
}
}
}
});
let am = ApplicationMonitor {
tx,
join_handle
};
am
}
pub fn get_snapshot(&self) -> HashMap<&'static str, i32> {
self.stats.clone()
}
}
Because rx
cannot be cloned, I must move
the references into the closure. When I do this, I am no longer able to access stats
outside of the thread.
I thought maybe I needed a second channel so the thread could communicate it's internals back out, but this doesn't work as i would need another thread to listen for that in a non-blocking way.
Is this where I'd use Arc
?
How can I have stats
live inside and out of the thread context?
Upvotes: 0
Views: 2144
Reputation: 2596
Yes, this is a place where you'd wrap your stats
in an Arc
so that you can have multiple references to it from different threads. But just wrapping in an Arc
will only give you a read-only view of the HashMap
- if you need to be able to modify it, you'll also need to wrap it in something which guarantees that only one thing can modify it at a time. So you'll probably end up with either an Arc<Mutex<HashMap<&'static str, i32>>>
or a Arc<RwLock<HashMap<&'static str, i32>>>
.
Alternatively, if you're just changing the values, and not adding or removing values, you could potentially use an Arc<HashMap<&static str, AtomicU32>>
, which would allow you to read and modify different values in parallel without needing to take out a Map
-wide lock, but Atomic
s can be a little more fiddly to understand and use correctly than locks.
Upvotes: 3