The Rust Programming Language - Chapter 16 fearless concurrency - 16.2 passing data between processes using messaging

Posted by AbsolutelyFreeW on Sun, 28 Nov 2021 16:40:31 +0100

16 fearless concurrent

Safe and efficient handling of concurrent programming is another major goal of Rust

Memory security and efficient programming have always been the goal of many languages. Rust uses ownership and type system to balance this

In this chapter, we will understand

1. How to create threads to run multi terminal code at the same time

2. Message passing concurrency, where channel is used to pass messages between threads

3. Shared state concurrency, in which multiple threads can access the same piece of data

4.Sync and Send trait extend the concurrency guarantee of Rust to user-defined types and types provided by the standard library

16.2 transferring data between processes using message passing

We use message passing to ensure concurrency security. One of the main tools to realize message passing concurrency in Rust is channel. The channel consists of sender and receiver. When either sender or receiver is discarded, the channel can be considered closed

Let's develop a program that generates values in one thread, sends them to the channel, receives values in another thread and prints them. This technology can help us realize the chat system or use multithreading for distributed computing and send some computing results to a thread for aggregation

use std::sync::mpsc;
fn main() {
    let (tx,rx) = mpsc::channel();
}

Create a channel and assign values at both ends to tx and rx respectively

Note that at this time, we just created a channel, but did not do anything. Of course, we can't compile

Here, use the mpsc::channel function to create a new channel; mpsc is an abbreviation for multiple producers and single consumers. This means that the sender can have multiple senders

mpsc::channel will return a tuple: the first element is the sender and the second element is the receiver. let statements and patterns are used to deconstruct this tuple

Let's try passing a string using a channel

use std::sync::mpsc;
use std::thread;
fn main() {
    let (tx,rx) = mpsc::channel();
    thread::spawn(move||{
        let val = String::from("Hi");
        tx.send(val).unwrap();
    });
    let received = rx.recv().unwrap();
    println!("Got:{}",received);
}
     Running `target/debug/smartPoint`
Got:Hi

We first moved tx to a new thread using move, sent a string in the new thread, and then received and printed it in the main thread. Perfect!

Channel and ownership transfer

Ownership rules play an important role in message passing, which helps us write safe concurrent code. Now let's try to continue using val after sending it

fn main() {
    let (tx,rx) = mpsc::channel();
    thread::spawn(move||{
        let val = String::from("Hi");
        tx.send(val).unwrap();
        println!("val is {}",val);
    });
    let received = rx.recv().unwrap();
    println!("Got:{}",received);
}
error[E0382]: borrow of moved value: `val`
 --> src/main.rs:8:30
  |
6 |         let val = String::from("Hi");
  |             --- move occurs because `val` has type `String`, which does not implement the `Copy` trait
7 |         tx.send(val).unwrap();
  |                 --- value moved here
8 |         println!("val is {}",val);
  |                              ^^^ value borrowed here after move

Sure enough, because the ownership of values has been transferred and can no longer be used, Rust's ownership rules are so comfortable

Send multiple values and observe the waiting of the receiver

use std::sync::mpsc;
use std::thread;
use std::time::Duration;
fn main() {
    let (tx,rx) = mpsc::channel();

    thread::spawn(move||{
        let vals = vec![
            String::from("hi"),
            String::from("from"),
            String::from("the"),
            String::from("thread"),
        ];
        for val in vals{
            tx.send(val).unwrap();
            thread::sleep(Duration::from_secs(1));
        }
    });
    for received in rx{
        println!("Got:{}",received);
    }
}
     Running `target/debug/smartPoint`
Got:hi
Got:from
Got:the
Got:thread

We see that sending multiple values can also be received one by one

Create multiple producers by cloning senders

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

    let tx1 =tx.clone();

    thread::spawn(move||{
        let vals = vec![
            String::from("hi"),
            String::from("from"),
            String::from("the"),
            String::from("thread"),
        ];
        for val in vals{
            tx1.send(val).unwrap();
            thread::sleep(Duration::from_secs(1));
        }
    });
    thread::spawn(move||{
        let vals = vec![
            String::from("more"),
            String::from("messages"),
            String::from("for"),
            String::from("you"),
        ];
        for val in vals{
            tx.send(val).unwrap();
            thread::sleep(Duration::from_secs(1));
        }
    });
    for received in rx{
        println!("Got:{}",received);
    }
}

We cloned a sender using the cloning method, and the sender can send data in another new thread. This is concurrency! Difficult and interesting

Topics: Back-end Concurrent Programming Rust