Реализация семантики потоков "move"

Я хочу написать функцию, которая будет вызываться так:

send("message","address");

Где какой-то другой поток, который делает

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

видит message,

В частности, сообщение может быть большим, и поэтому я бы хотел семантику "переместить" или "нулевую копию" для отправки сообщения.

В C решение выглядит примерно так:

  1. Размещать сообщения в куче
  2. Иметь глобальную, поточно-ориентированную хэш-карту, которая отображает "адрес" в некотором месте памяти
  3. Записать указатели в ячейку памяти при отправке и разбудить получателя с помощью семафора
  4. Чтение указателей из области памяти при получении и ожидание семафора для обработки новых сообщений

Но, согласно другому такому вопросу, шаг № 2 " звучит как плохая идея". Поэтому я бы хотел увидеть более идиоматический способ решения этой проблемы.

1 ответ

Решение

Вы автоматически получаете такую ​​семантику перемещения и получаете легкие движения, помещая большие значения в 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<...>Так как глобальное состояние может усложнить рассуждения о поведении программы. Он также использует RingBuf как очередь, так что сообщения банка для данного address, Если вы хотите поддерживать только одно сообщение за раз, тип может быть ConcurrentHashMap<String, String>, send мог стать MAP.lock().insert(address, message) а также recv просто MAP.lock().remove(address),

(NB. Я не скомпилировал это, поэтому типы могут не совпадать точно.)

Другие вопросы по тегам