如何在线程内自我变异?

时间:2019-01-06 01:55:08

标签: multithreading rust

我正在编写一个应用程序,该应用程序应该循环地从串行端口读取(例如观察程序),并且还应该向其中写入命令。

只允许主线程写入,而创建的线程只能读取。

我创建了一个简单的示例来在这里重现我的问题。 tx_thread循环读取串行端口,并在一定条件下通过MPSC通道发送消息。 rx_thread查找消息;当有可用的东西时,它会进行处理,并且还应该更改该结构的当前状态。

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

// this will also implement Drop trait to wait threads to
// be finished (message will be Enum instead of number in this case)

#[derive(Debug)]
struct MyStruct {
    num: u32,
    tx_thread: Option<thread::JoinHandle<()>>,
    rx_thread: Option<thread::JoinHandle<()>>,
}

impl MyStruct {
    fn new() -> MyStruct {
        MyStruct {
            num: 0,
            tx_thread: None,
            rx_thread: None,
        }
    }

    fn start(&mut self) {
        let (tx, rx) = mpsc::channel();

        // tx thread will read from serial port infinitely,
        // and send data to mpsc channel after certain condition
        // to be processed.
        let tx_thread = thread::spawn(move || {
            let mut i = 0;

            loop {
                tx.send(i).unwrap();
                i += 1;
                thread::sleep(Duration::from_secs(1));
            }
        });

        // after this will receive message, it will start
        // processing and mutate `self` state if needed.
        let rx_thread = thread::spawn(move || loop {
            let num = rx.recv().unwrap();
            println!("{:?}", num);

            /* here, how do I save `num` to `self`? */

            thread::sleep(Duration::from_secs(1));
        });

        self.tx_thread = Some(tx_thread);
        self.rx_thread = Some(rx_thread);
    }
}

fn main() {
    let mut s = MyStruct::new();
    s.start();
    thread::sleep(Duration::from_secs(999999));
}

1 个答案:

答案 0 :(得分:1)

在不和谐频道上,一个了不起的家伙(断了笔)告诉了我很多很好的解决方案,所有功劳都归功于他。

因此解决方案是将我们想要突变的属性放到Arc<Mutex<>>中,然后将克隆的引用移到线程中。因此基本上,代码将如下所示:

use std::sync::{mpsc, Arc, Mutex};
use std::thread;
use std::time::Duration;

type MyType = Arc<Mutex<u32>>;

#[derive(Debug)]
struct MyStruct {
    num: MyType,
    tx_thread: Option<thread::JoinHandle<()>>,
    rx_thread: Option<thread::JoinHandle<()>>,
}

impl MyStruct {
    fn new() -> MyStruct {
        MyStruct {
            num: Arc::new(Mutex::new(0)),
            tx_thread: None,
            rx_thread: None,
        }
    }

    fn start(&mut self) {
        let (tx, rx) = mpsc::channel();

        // tx thread will read from serial port infinitely,
        // and send data to mpsc channel after certain condition
        // to be processed.
        let tx_thread = thread::spawn(move || {
            let mut i = 0;

            loop {
                tx.send(i).unwrap();
                i += 1;
                thread::sleep(Duration::from_secs(1));
            }
        });

        // clone here.
        let arc_num = self.num.clone();
        let rx_thread = thread::spawn(move || loop {
            let num = rx.recv().unwrap();
            // println!("{:?}", num);

            // now we can use it for writing/reading.
            *arc_num.lock().unwrap() = num;
            println!("{:?}", *arc_num.lock().unwrap());

            thread::sleep(Duration::from_secs(1));
        });

        self.tx_thread = Some(tx_thread);
        self.rx_thread = Some(rx_thread);
    }
}

fn main() {
    let mut s = MyStruct::new();
    s.start();
    thread::sleep(Duration::from_secs(999999));
}

编辑:另一种解决方案是使用Arc<Mutex<>>创建内部结构并在那里进行工作,从而使您可以访问所需的所有内容。

请参见下面的代码:

use std::default::Default;
use std::sync::{mpsc, Arc, Mutex};
use std::thread;
use std::time::Duration;

// this will also implement Drop trait to wait threads to
// be finished (message will be Enum instead of number in this case)

#[derive(Debug, Default)]
struct MyStructInner {
    num: u32,
    tx_thread: Option<thread::JoinHandle<()>>,
    rx_thread: Option<thread::JoinHandle<()>>,
}

#[derive(Debug, Default)]
struct MyStruct {
    inner: Arc<Mutex<MyStructInner>>,
}

impl MyStruct {
    fn new() -> MyStruct {
        MyStruct {
            inner: Arc::new(Mutex::new(MyStructInner {
                num: 0,
                ..Default::default()
            })),
        }
    }

    fn start(&mut self) {
        let (tx, rx) = mpsc::channel();

        // tx thread will read from serial port infinitely,
        // and send data to mpsc channel after certain condition
        // to be processed.
        let tx_thread = thread::spawn(move || {
            let mut i = 0;

            loop {
                tx.send(i).unwrap();
                i += 1;
                thread::sleep(Duration::from_secs(1));
            }
        });

        // after this will receive message, it will start
        // processing and mutate `self` state if needed.
        let local_self = self.inner.clone();
        let rx_thread = thread::spawn(move || loop {
            let num = rx.recv().unwrap();

            local_self.lock().unwrap().num = num;
            println!("{:?}", local_self.lock().unwrap().num);

            thread::sleep(Duration::from_secs(1));
        });

        self.inner.lock().unwrap().tx_thread = Some(tx_thread);
        self.inner.lock().unwrap().rx_thread = Some(rx_thread);
    }
}

fn main() {
    let mut s = MyStruct::new();
    s.start();
    thread::sleep(Duration::from_secs(999999));
}