如何创建可以在 Rust 多线程服务器中使用的结构?

Posted

技术标签:

【中文标题】如何创建可以在 Rust 多线程服务器中使用的结构?【英文标题】:How do I create a structure that can be used in a Rust multithreaded server? 【发布时间】:2016-11-03 07:01:48 【问题描述】:

我想实现一个简单的服务器,供我项目的 3 个不同模块使用。 这些模块会将数据发送到服务器,服务器会将其保存到文件中,并在这些模块完成工作时合并这些信息。 所有这些信息都有一个时间戳(一个浮点数)和一个标签(一个浮点数或一个字符串)。

这是我保存这些信息的数据结构:

pub struct Data 
    file_name: String,
    logs: Vec<(f32, String)>,
    measures: Vec<(f32, f32)>,
    statements: Vec<(f32, String)>,

我使用socket与服务器交互。

我还使用 Arc 来实现 Data 结构并使其可被每个模块共享。 所以,当我处理客户端时,我会验证模块发送的消息是否正确,如果是,我会调用一个新函数来处理并将消息保存在良好的数据结构字段(logsmeasuresstatements)。

// Current ip address
let ip_addr: &str = &format!(":",
                             &ip,
                             port);

// Bind the current IP address
let listener = match TcpListener::bind(ip_addr) 
    Ok(listener) => listener,
    Err(error) => panic!("Canno't bind , due to error ",
                         ip_addr,
                         error),
;

let global_data_struct = Data::new(DEFAULT_FILE.to_string());
let global_data_struct_shared = Arc::new(global_data_struct);

// Get and process streams
for stream in listener.incoming() 
    let mut global_data_struct_shared_clone = global_data_struct_shared.clone();
    thread::spawn(move || 
        // Borrow stream
        let stream = stream;
        match stream 
            // Get the stream value
            Ok(mut stream_v) => 
                let current_ip = stream_v.peer_addr().unwrap().ip();
                let current_port = stream_v.peer_addr().unwrap().port();
                println!("Connected with peer :", current_ip, current_port);
                // PROBLEM IN handle_client!
                // A get_mut from global_data_struct_shared_clone
                // returns to me None, not a value - so I
                // can't access to global_data_struct_shared_clone
                // fields :'(
                handle_client(&mut stream_v, &mut global_data_struct_shared_clone);
            ,
            Err(_) => error!("Canno't decode stream"),
        
    );


// Stop listening
drop(listener);

我在获取handle_client 中的可变引用以处理global_data_struct_shared_clone 中的字段时遇到了一些问题,因为Arc::get_mut(global_data_struct_shared_clone) 返回给我None - 由于每个传入请求的global_data_struct_shared.clone()

有人可以帮我正确管理这 3 个模块之间的结构吗?

【问题讨论】:

您尝试过使用Arc&lt;Mutex&lt;...&gt;&gt; 吗? 不,因为我认为使用Mutex 会遇到问题,特别是因为我的任何模块都不会访问模块已经存在的字段。每个模块在Data 中都有自己的字段 - 所以将MutexArc 将无用地阻止我的结构,对吧...? 它只在处理时阻塞,但你可以用更精细的方式完美地做到这一点:) 好的,我会试试的,谢谢 :-) 如果可行,我会发布解决方案。 好的,我尝试将MutexArc 一起使用,它似乎有效!下一级:粒度...再次非常感谢 ;-) 【参考方案1】:

Rust 的见解是,内存安全是通过强制执行 Aliasing XOR Mutability 来实现的。

执行这一单一原则可以防止所有类别的错误:指针/迭代器失效(这是目标)以及数据竞争


Rust 将尽可能在编译时强制执行这一原则;但是,如果用户使用专用类型/方法选择加入,它也可以在运行时强制执行。

Arc::get_mut就是这样一种方法。 Arc(原子引用计数指针)专门用于在多个所有者之间共享引用,这意味着别名,因此默认情况下不允许可变性; Arc::get_mut 将执行运行时检查:如果指针实际上不是别名(计数为 1),则它允许可变性。

但是,正如您所意识到的,这不适合您的情况,因为 Arc 在那个时间点是别名。


所以你需要转向其他类型。

最简单的解决方案是Arc&lt;Mutex&lt;...&gt;&gt;Arc 允许共享,Mutex 允许受控可变性,您可以与Mutex 强制执行的运行时受控可变性一起共享。

这是粗粒度的,但可能就足够了。

更复杂的方法可以使用RwLock(读写锁),更细粒度的Mutex甚至原子;但我建议从一个 Mutex 开始,看看效果如何,你必须先走路再跑。

【讨论】:

以上是关于如何创建可以在 Rust 多线程服务器中使用的结构?的主要内容,如果未能解决你的问题,请参考以下文章

Rust多线程之数据共享

如何在 Rust 的结构中存储闭包?

如何在结构/流程图中表示多个线程

如何在不使用复制或克隆的情况下克隆Rust中的结构?

「Rust语言」用Rust构建微服务

Rust语言的多线程编程