Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Message passing

Một cách phổ biến để các luồng phối hợp an toàn: thay vì cùng truy cập chung một vùng dữ liệu, chúng gửi giá trị cho nhau qua một kênh (channel) — “không chia sẻ bộ nhớ để giao tiếp, mà giao tiếp bằng cách chuyển quyền sở hữu qua lại”.

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

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

    thread::spawn(move || {
        let gia_tri = String::from("xin chào");
        tx.send(gia_tri).unwrap();
    });

    let nhan_duoc = rx.recv().unwrap();
    println!("Nhận được: {nhan_duoc}");
}

mpsc::channel() (multiple producer, single consumer) trả về cặp (Sender, Receiver). tx.send(v) move quyền sở hữu v vào kênh — đây là điểm mấu chốt: sau khi gửi, luồng gửi không còn quyền truy cập v nữa, nên không thể có chuyện cả luồng gửi lẫn luồng nhận cùng đọc/ghi cùng một giá trị cùng lúc. Borrow checker biến quy tắc thiết kế “đừng chia sẻ dữ liệu đang gửi” thành một ràng buộc bắt buộc, không phải quy ước hi vọng người viết code tự tuân theo:

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

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

    thread::spawn(move || {
        let gia_tri = String::from("xin chào");
        tx.send(gia_tri).unwrap();
        println!("vẫn dùng gia_tri: {gia_tri}"); // lỗi: gia_tri đã bị move vào send
    });

    println!("{}", rx.recv().unwrap());
}

rx.recv() chặn luồng gọi tới khi có giá trị, trả ResultErr khi mọi Sender đã bị drop (sẽ không bao giờ có giá trị mới gửi tới nữa). Có thể duyệt rx trực tiếp như iterator, tự động chặn chờ ở mỗi lần lặp và kết thúc khi kênh đóng:

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

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

    thread::spawn(move || {
        let cac_gia_tri = vec![
            String::from("xin"),
            String::from("chào"),
            String::from("từ"),
            String::from("luồng con"),
        ];

        for gia_tri in cac_gia_tri {
            tx.send(gia_tri).unwrap();
            thread::sleep(Duration::from_millis(1));
        }
    });

    for nhan_duoc in rx {
        println!("Nhận được: {nhan_duoc}");
    }
}

Nhiều luồng gửi

Sender implement Clone — nhân bản để nhiều luồng cùng gửi vào một Receiver:

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

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

    thread::spawn(move || {
        tx.send(String::from("từ luồng 1")).unwrap();
    });

    thread::spawn(move || {
        tx2.send(String::from("từ luồng 2")).unwrap();
    });

    for nhan_duoc in rx {
        println!("Nhận được: {nhan_duoc}");
    }
}