实施"移动"线程语义

时间:2015-01-27 20:57:48

标签: multithreading rust messaging zero-copy

我想编写一个像这样调用的函数:

send("message","address");

正在做的其他一些线程

let k = recv("address");
println!("{}",k);

看到message

特别是,消息可能很大,所以我喜欢"移动"或"零拷贝"用于发送消息的语义。

在C中,解决方案类似于:

  1. 在堆上分配消息
  2. 拥有一个全局的线程安全散列图,用于映射"地址"到一些记忆位置
  3. 在发送时将指针写入内存位置,并使用信号量唤醒接收器
  4. 在接收时从内存位置读取指针,并等待信号量处理新消息
  5. 但根据另一个SO问题,步骤#2" sounds like a bad idea"。因此,我希望看到一种更具Rust风格的方法来解决这个问题。

1 个答案:

答案 0 :(得分:2)

您可以自动获得这些移动语义,并通过将大值放入Box(即在堆上分配它们)来实现轻量级移动。使用type ConcurrentHashMap<K, V> = Mutex<HashMap<K, V>>;作为线程安全散列图(可以通过各种方式改进),可能有:

use std::collections::{HashMap, RingBuf};
use std::sync::Mutex;

type ConcurrentHashMap<K, V> = Mutex<HashMap<K, V>>;

lazy_static! {
    pub static ref MAP: ConcurrentHashMap<String, RingBuf<String>> = {
        Mutex::new(HashMap::new())
    }
}

fn send(message: String, address: String) {
    MAP.lock()
       // find the place this message goes
       .entry(address)
       .get()
       // create a new RingBuf if this address was empty
       .unwrap_or_else(|v| v.insert(RingBuf::new()))
       // add the message on the back
       .push_back(message)
}
fn recv(address: &str) -> Option<String> {
     MAP.lock()
        .get_mut(address)
        // pull the message off the front
        .and_then(|buf| buf.pop_front())
}

该代码使用lazy_static!宏来实现全局散列图(使用包含Arc<ConcurrentHashMap<...>的本地对象可能更好,fwiw,因为全局状态可以很难推理程序行为)。它还使用RingBuf作为队列,以便消息为给定的address进行存储。如果您只希望一次支持一封邮件,则类型可以是ConcurrentHashMap<String, String>send可以MAP.lock().insert(address, message)recv只能MAP.lock().remove(address)

(注意:我没有编译过这个,所以类型可能不完全匹配。)