Rust learning record -> Channel communication between threads


Preface

After finishing the study of threads, the author continued to learn about multi-thread communication tools, that is, channels. Channels are used to achieve communication between many different types of multi-threads, which also triggered some of the author's thoughts, as follows: stated.
The same learning video address is ->> B station Linghu Yichong Rust advanced


1. What is the pipeline?

One of the main tools for achieving message-passing concurrency in Rust is channels. The channel consists of two parts, one is the sender and the other is the receiver. The sender is used to send messages, and the receiver is used to receive messages. A channel is considered closed when either the sender or receiver is discarded.

2. Usage steps

1. Channel type

1. Create a channel through mpsc::channel(), mpsc is multiple producers and one consumer;
2. Create a channel through SPMC::channel(), spmc is one producer and multiple consumers;

Note: After creating a channel, the sender and receiver (producer and consumer) are returned. Example:

let (tx,rx) = mpsc::channel();
let (tx,rx) = spmc::channel();

In the following, multiple producers and a single consumer will be used as the main example.

2. Definition and use of channels

code show as below:

use std::thread;
use std::sync::mpsc;

fn main() {
    
    
    let (tx, rx) = mpsc::channel();

    thread::spawn(move || {
    
    
        let val = String::from("hello,thread!");
        tx.send(val).unwrap();
    });

    let received = rx.recv().unwrap();
    println!("Main thread got {}",received);
}

In the above code snippet, the author defines (tx, rx) through mpsc::channel(), which is the sender and receiver. At the same time, a child thread is created, a String type variable is defined in the thread and passed through tx The sending end sends the variable into the channel, and the rx receiving end defined in the main thread will receive the variable in the channel and finally output it. The compilation and running results are as follows:

root@ThinkPad-T540p:~/learn_rust/learn_thread_channel# cargo run
   Compiling learn_thread_channel v0.1.0 (/root/learn_rust/learn_thread_channel)
    Finished dev [unoptimized + debuginfo] target(s) in 0.82s
     Running `target/debug/learn_thread_channel`
Main thread got hello,thread!

The compilation and running was smooth, and the communication between the main thread and the sub-thread was successfully realized. However, in the previous article about threads, the author once encountered a situation where the sub-thread was forcibly interrupted because the main thread ended before it ended. At that time, join() was used to solve this problem, but join() was not used here, but the main thread kept waiting for the sub-thread to send the val variable, which was contrary to the knowledge learned yesterday.

For this question, the answer is also obtained in the video, because the recv() function used by the receiving end rx. The recv() method used here will keep the main thread blocked until a message arrives and is received, so there is no need for the join() method in the above code snippet. You can also use the try_recv() method, which will not block. , will return immediately.

3. Ownership issues in channels

In the above simple code segment for channel communication, the author sends the String variable val as the sending data to the sending channel. At this time, it is equivalent to moving the ownership of val to the channel, so after the channel sends val , you can no longer use val alone.

4. Multi-information multi-thread simulation

After finishing the above study, in addition to the simple implementation of communication, we also implemented the situation of a single sub-thread sending multiple messages and multiple threads sending messages. The code is as follows:

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

fn main() {
    
    
    let (tx,rx) = mpsc::channel();

    thread::spawn(move || {
    
    
        let vals = vec![
            String::from("Hello"),
            String::from("nice"),
            String::from("to"),
            String::from("meet"),
            String::from("you!"),
        ];

        for val in vals {
    
    
            tx.send(val).unwrap();
            thread::sleep(Duration::from_secs(1));
        }
    });

    for recv in rx {
    
    
        println!("{}",recv);
    }
    println!("Hello, world!");
}

The above code uses the vec container and sends multiple messages to the main thread in a single sub-thread. The compilation and running results are as follows:

root@ThinkPad-T540p:~/learn_rust/learn_thread_channel3# cargo run
    Finished dev [unoptimized + debuginfo] target(s) in 0.06s
     Running `target/debug/learn_thread_channel3`
Hello
nice
to
meet
you!
Hello, world!

Similarly, to implement multiple threads, the code is as follows:

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

fn main() {
    
    
    let (tx, rx) = mpsc::channel();
    let tx1 = mpsc::Sender::clone(&tx);
    let tx2 = mpsc::Sender::clone(&tx);
    thread::spawn(move || {
    
    
        let vals = vec![
            String::from("1"),
            String::from("2"),
            String::from("3"),
            String::from("4"),
        ];
        for val in vals {
    
    
            tx.send(val).unwrap();
            thread::sleep(Duration::from_secs(1));
        }
    });

    thread::spawn(move || {
    
    
        let vals = vec![
            String::from("a"),
            String::from("b"),
            String::from("c"),
            String::from("d"),
        ];
        for val in vals {
    
    
            tx1.send(val).unwrap();
            thread::sleep(Duration::from_secs(1));
        }
    });

    thread::spawn(move || {
    
    
            let vals = vec![
            String::from("A"),
            String::from("B"),
            String::from("C"),
            String::from("D"),
        ];
        for val in vals {
    
    
            tx2.send(val).unwrap();
            thread::sleep(Duration::from_secs(1));
        }
    });

    for recv in rx {
    
    
        println!("{}",recv);
    }
    println!("Hello, world!");
}

The compilation and running results are as follows:

root@ThinkPad-T540p:~/learn_rust/learn_thread_channel4# cargo run
   Compiling learn_thread_channel4 v0.1.0 (/root/learn_rust/learn_thread_channel4)
    Finished dev [unoptimized + debuginfo] target(s) in 1.74s
     Running `target/debug/learn_thread_channel4`
1
a
A
b
2
B
3
c
C
d
4
D
Hello, world!

It can be observed that the information sent by multiple threads is received by the main thread in turn.


Summarize

The above is the whole process of my channel learning. I recently wrote a small example of cyclic communication reply between threads. After it is completed, it will be added to the article for learning reference. I also ask all readers to give me your advice, criticism and corrections.

Guess you like

Origin blog.csdn.net/weixin_45704680/article/details/121131377