简体   繁体   English

在 Rust 中的线程之间共享可变数据

[英]Sharing Mutable Data Between Threads in Rust

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.我想要一个接受和处理事件的 http 服务器。 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.在接收/处理事件时,我希望EventManagerApplicationMonitor发送更新,以跟踪已接受/处理了多少事件。 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 . ApplicationMonitor还将(最终)处理诸如跟踪并发连接数之类的事情,但在此示例中,我只希望我的EventManager向我的ApplicationMonitor发送Inc('event_accepted')更新。

To be useful, I need the ApplicationMonitor to be able to return a snapshot of the stats when the requested through a /stats route.为了有用,我需要ApplicationMonitor能够在通过/stats路由请求时返回统计信息的快照。

在此处输入图像描述

So I have an ApplicationMonitor which spawns a thread and listens on a channel for incoming Stat events.所以我有一个ApplicationMonitor ,它产生一个线程并在通道上监听传入的Stat事件。 When it receives a Stat event it updates the stats HashMap.当它接收到一个Stat事件时,它会更新统计数据 HashMap。 The stats hashmap must be mutable within both ApplicationMonitor as well as the spawned thread.统计信息 hashmap 在ApplicationMonitor和衍生线程中必须是可变的。

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.因为rx不能被克隆,我必须move引用移动到闭包中。 When I do this, I am no longer able to access stats outside of the thread.当我这样做时,我不再能够访问线程之外的stats

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 ?这是我使用Arc的地方吗?

How can I have stats live inside and out of the thread context?如何让stats在线程上下文内外存在?

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.是的,这是您将stats包装在Arc中的地方,这样您就可以从不同的线程中对它进行多次引用。 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.但是仅仅包裹在Arc中只会给你一个HashMap的只读视图——如果你需要能够修改它,你还需要把它包裹在一个保证只有一个东西可以修改它的东西中时间。 So you'll probably end up with either an Arc<Mutex<HashMap<&'static str, i32>>> or a Arc<RwLock<HashMap<&'static str, i32>>> .因此,您最终可能会得到Arc<Mutex<HashMap<&'static str, i32>>>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.或者,如果您只是更改值,而不是添加或删除值,您可能会使用Arc<HashMap<&static str, AtomicU32>> ,这将允许您并行读取和修改不同的值而无需采取出一个Map范围的锁,但是Atomic比锁更容易理解和正确使用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM