如何创建一个全局可变的布尔状态标志

4

前言:我已经进行了研究,知道单独使用全局变量不是一个好主意,也不符合Rust的惯用法。非常乐意听取其他解决此问题的建议。

背景:我有一个控制台应用程序,连接到websocket后,服务器会发送“已连接”消息。发送方和接收方是分开的线程,一切都运作良好。在connect()调用之后,循环开始,并在终端中放置提示,表示应用程序已准备好从用户接收输入。

问题:问题在于当前的执行流程调用连接,然后立即显示提示,然后应用程序接收来自服务器的连接成功消息。

在其他高级语言中如何解决:设置一个全局布尔值(我们称其为ready),一旦应用程序“准备就绪”,就显示提示。

我认为在Rust中可能会是这样:

//Possible global ready flag with 3 states (true, false, None)
let ready: Option<&mut bool> = None;

fn main(){
    welcome_message(); //Displays a "Connecting..." message to the user

    //These are special callback I created and basically when the
    //message is received the `connected` is called.
    //If there was an error getting the message (service is down)
    //then `not_connected` is called. *This is working code*
    let p = mylib::Promise::new(connected, not_connected);

    //Call connect and start websocket send and receive threads
    mylib::connect(p);

    //Loop for user input
    loop {
        match ready {
            Some(x) => {
                if x == true { //If ready is true, display the prompt
                    match prompt_input() {
                        true => {},
                        false => break,
                    }
                } else {
                    return; //If ready is false, quit the program
                }
            },
            None => {} //Ready is None, so continue waiting
        }
    }
}

fn connected() -> &mut bool{
    println!("Connected to Service! Please enter a command. (hint: help)\n\n");
    true
}

fn not_connected() -> &mut bool{
    println!("Connection to Service failed :(");
    false
}

问题: 你会如何在Rust中解决这个问题?我尝试了将它传递给所有库方法调用,但遇到了一些关于在FnOnce()闭包中借用不可变对象的主要问题。


虽然我鼓励你不要使用全局可变状态,但如果我不至少指向这个答案,我会感到遗漏的。这个答案展示了如何做到这一点:https://dev59.com/4V4c5IYBdhLWcg3wjq7R - Shepmaster
1个回答

3

听起来你希望有两个线程通过通道进行通信。看看这个例子:

use std::thread;
use std::sync::mpsc;
use std::time::Duration;

enum ConsoleEvent {
    Connected,
    Disconnected,
}

fn main() {
    let (console_tx, console_rx) = mpsc::channel();

    let socket = thread::spawn(move || {
        println!("socket: started!");

        // pretend we are taking time to connect
        thread::sleep(Duration::from_millis(300));

        println!("socket: connected!");
        console_tx.send(ConsoleEvent::Connected).unwrap();

        // pretend we are taking time to transfer
        thread::sleep(Duration::from_millis(300));

        println!("socket: disconnected!");
        console_tx.send(ConsoleEvent::Disconnected).unwrap();

        println!("socket: closed!");
    });

    let console = thread::spawn(move || {
        println!("console: started!");

        for msg in console_rx.iter() {
            match msg {
                ConsoleEvent::Connected => println!("console: I'm connected!"),
                ConsoleEvent::Disconnected => {
                    println!("console: I'm disconnected!");
                    break;
                }
            }
        }
    });

    socket.join().expect("Unable to join socket thread");
    console.join().expect("Unable to join console thread");
}

在这里,有三个线程在运行:
  1. 主线程。
  2. 一个从“socket”读取数据的线程。
  3. 与用户交互的线程。
每个线程都可以维护自己的非共享状态。这样可以更容易地推理每个线程。这些线程使用通道(channel)来安全地在它们之间发送更新。跨线程传输的数据封装在一个枚举中。
当我运行这个程序时,我得到。
socket: started!
console: started!
socket: connected!
console: I'm connected!
socket: disconnected!
socket: closed!
console: I'm disconnected!

是的,这非常有道理。非常感谢(再次)。这是一种更加干净简洁的做法。正如我所读到的,通常会有比使用全局变量更好的方法,只是我想不出来。 - SnareChops

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接