Круговая ссылка между двумя структурами с высокой производительностью - PullRequest
0 голосов
/ 09 июля 2019

У меня есть две структуры Node и Communicator. Node содержит «бизнес-логику», а Communicator содержит методы для отправки и получения UDP-сообщений. Node должен вызывать методы на Communicator, когда он хочет отправлять сообщения, а Communicator должен вызывать методы на Node, когда он получает UDP-сообщение. Если они имеют одинаковую структуру, проблем нет вообще. Но я хочу отделить их, потому что у них явно разные обязанности. Наличие всего этого в одной структуре стало бы неуправляемым. Мой код выглядит следующим образом:

fn main() {
    use std::sync::{Arc, Mutex, Condvar, Weak};
    use std::thread;


    pub struct Node {
        communicator: Option<Arc<Communicator>>
    }

    impl Node {
        pub fn new() -> Node {
            Node {
                communicator: None
            }
        }

        pub fn set_communicator(&mut self, communicator: Arc<Communicator>) {
            self.communicator = Some(communicator);
        }
    }

    pub struct Communicator {
        node: Option<Weak<Node>>
    }

    impl Communicator {
        pub fn new() -> Communicator {
            Communicator {
                node: None
            }
        }

        pub fn set_node(&mut self, node: Weak<Node>) {
            self.node = Some(node);
        }
    }



    let mut my_node = Arc::new(Node::new());
    let mut my_communicator = Arc::new(Communicator::new());

    Arc::get_mut(&mut my_node).unwrap().set_communicator(Arc::clone(&my_communicator));

    //Arc::get_mut(&mut my_communicator).unwrap().set_node(Arc::downgrade(&my_node));
}

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

Я вижу пару вариантов:

  • Используйте Mutex или RwLock, чтобы получить внутреннюю изменчивость. Но это дает снижение производительности.
  • Используйте Cell или RefCell. Но они не поточнобезопасны.
  • Используйте AtomicCell из crossbeam. Похоже, лучший вариант до сих пор. Но как насчет производительности?
  • Используйте unsafe. Но где? И как сделать так, чтобы код оставался безопасным для памяти, несмотря на unsafe?

Теоретически я мог бы разбить Communicator на Sender и Receiver, чтобы не было циклических ссылок. Но для моей программы я знаю, что в будущем у меня будет похожая ситуация, когда это будет невозможно.

Похоже, что поскольку я просто разбил структуру на две, по причинам структурирования кода и не получая новых функциональных возможностей, должен быть способ сделать это без необходимости платить какие-либо потери производительности, как Mutex. 1040 *

...