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

Send và Sync

Hai trait đánh dấu (marker trait — không có method nào, chỉ tồn tại để gắn nhãn cho kiểu) là nền tảng thật sự khiến “concurrency không sợ hãi” thành hiện thực: SendSync. Phần lớn thời gian không cần biết tới chúng, vì trình biên dịch tự động implement cả hai cho mọi kiểu cấu thành hoàn toàn từ các phần đã là Send/Sync — nhưng chính hai trait này là lý do những lỗi như “dùng Rc giữa nhiều luồng” bị chặn ngay lúc biên dịch ở chương trước.

Send: chuyển quyền sở hữu qua luồng khác an toàn

Một kiểu implement Send nếu quyền sở hữu giá trị của nó chuyển được sang một luồng khác an toàn — hầu hết kiểu trong Rust là Send. Rc<T> là ngoại lệ đáng chú ý: không implement Send, vì bộ đếm bên trong nó không đồng bộ hoá — chuyển một Rc sang luồng khác trong khi luồng gốc vẫn giữ bản sao khác sẽ mở đường cho hai luồng cùng tăng/giảm đếm không an toàn. Đây chính xác là lý do thread::spawn đòi hỏi closure phải 'static và mọi giá trị move vào phải Send — cố move một Rc<T> vào một closure cho thread::spawn là lỗi biên dịch, không phải lỗi runtime ẩn.

Sync: chia sẻ tham chiếu qua luồng khác an toàn

Một kiểu implement Sync nếu &T (tham chiếu bất biến tới nó) an toàn để nhiều luồng cùng cầm — nói cách khác, TSync khi và chỉ khi &TSend. Mutex<T>Sync (đó là toàn bộ lý do nó tồn tại — tự đồng bộ hoá truy cập). RefCell<T> thì không — nó chỉ kiểm tra quy tắc mượn lúc chạy trong ngữ cảnh một luồng duy nhất, không có cơ chế đồng bộ nào chống lại hai luồng cùng gọi borrow_mut() đồng thời. Đây là lý do chia sẻ trạng thái khả biến giữa các luồng cần Mutex<T>, chứ không dùng lại được RefCell<T>.

Vì sao điều này quan trọng

SendSync biến “kiểu dữ liệu này có an toàn để dùng giữa nhiều luồng hay không” từ một câu hỏi phải tự nhớ và kiểm tra thủ công, thành một thuộc tính của hệ thống kiểu mà trình biên dịch tự kiểm tra ở mọi nơi kiểu đó được dùng — cùng triết lý với quyền sở hữu và borrow checker: thay vì tài liệu hoá quy tắc rồi hy vọng không ai viết sai, mã hoá thẳng quy tắc đó vào kiểu để trình biên dịch từ chối biên dịch code vi phạm.

Cả hai trait đều tự động derive — không tự tay implement cho kiểu của mình trong phần lớn trường hợp, trừ khi đang viết code cấp thấp dùng unsafe để tự quản lý một thứ vốn không an toàn giữa nhiều luồng theo mặc định (nói ở chương Tính năng nâng cao) — tự implement Send/Sync thủ công là một cam kết “tôi tự đảm bảo điều này an toàn”, trình biên dịch không tự kiểm tra giúp được nữa một khi đã dùng unsafe impl.