Pipeline patterns are common in programming. Use the Linux command line, often used pipeline connecting the two commands: such as ls/TMP | grep hello. TXT, find/TMP directory exists hello. TXT file. The pipeline operator is a two-command pipeline that uses the output of the ls command line as the input of the grep command line to make the data flow. Back in programming, the benefit of data flow is that the programming logic can be split up, with different threads (coroutines, processes) implementing different logic modules, making coding easier.
Write an example of Rust to practice:
use std::thread;
use std::sync::mpsc;
fn run_thread_1() -> (thread::JoinHandle<()>, mpsc::Receiver<String>) {
let (tx, rx) = mpsc::channel();
let hdl = thread::spawn(move || {
tx.send("thread_1".to_owned()).unwrap();
});
(hdl, rx)
}
fn run_thread_2(rx1: mpsc::Receiver<String>) -> (thread::JoinHandle<()>, mpsc::Receiver<String>) {let (tx, rx) = mpsc::channel();
let hdl = thread::spawn(move| | {if let Ok(mut msg) = rx1.recv() {
msg.push_str("-> thread_2"); tx.send(msg).unwrap(); }}); (hdl, rx) }fn run_thread_3(rx2: mpsc::Receiver<String>) -> thread::JoinHandle<()> {
let hdl = thread::spawn(move| | {if let Ok(msg) = rx2.recv() {
println!("{} -> thread_3", msg); }}); hdl }fn main() {
let mut thread_v = vec![];
let (hdl, rx1) = run_thread_1();
thread_v.push(hdl);
let (hdl, rx2) = run_thread_2(rx1);
thread_v.push(hdl);
let hdl = run_thread_3(rx2);
thread_v.push(hdl);
for hdl inthread_v { hdl.join().unwrap(); }}Copy the code
The example program creates three threads. Thread 1 sends data to thread 2, thread 2 sends data to thread 3, thread 3 prints data, and the main thread waits for the three threads to return. The program connects the data streams of three threads through channels, just like the production line in a factory. A raw material is processed through various processes and finally forms products.
digression
- Rust channels transmit data through ownership transfers, and threads communicate at a low cost. There is also a PIPE system call in C that passes data (deep-copy data) through the kernel buffer and is generally used for interprocess communication.
- It is also not difficult to implement features like Rust/Go channel between threads in C. Through MUtex and COND, thread blocking waiting is realized, and through spin lock (no lock) queue, buffer data function is realized. The sender adds data to queue, triggers condition variable to wake up thread, and thread receives data from queue, thus realizing channel function. Queues pass data by holding Pointers to data, in effect more like a Rust Channel transfer of ownership.