https://zhuanlan.zhihu.com/p/50101525
本文分析Rust標准庫中的channel,channel(通道)作為線程間通信的一種方式被廣泛使用。
Rust提供了多生產者單消費者的channel。我們重點關注多個生產者的情況。
它的實現方式非常有趣。我把它分為通道升級跟並發隊列兩部分。
本文描述通道升級
對於一個channel()調用,我們得到的(sender, receiver)是oneshot的,這一點從源碼可以得到暗示:
#[stable(feature = "rust1", since = "1.0.0")] pub fn channel<T>() -> (Sender<T>, Receiver<T>) { let a = Arc::new(oneshot::Packet::new()); (Sender::new(Flavor::Oneshot(a.clone())), Receiver::new(Flavor::Oneshot(a))) }
這里至少有四個結構:
- oneshot::Packet:Packet,真正存放數據的地方。此處是單個數據(其他類型可能使用隊列)
- Flavor::Oneshot。
- Sender/Receiver。
我們分別看下他們的數據結構源碼,首先是oneshot::Packet,它位於mpsc/oneshot.rs:
pub struct Packet<T> { // Internal state of the chan/port pair (stores the blocked thread as well) state: AtomicUsize, // One-shot data slot location data: UnsafeCell<Option<T>>, // when used for the second time, a oneshot channel must be upgraded, and // this contains the slot for the upgrade upgrade: UnsafeCell<MyUpgrade<T>>, }
可以看出data是為一個數據准備的。upgrade字段用於通道升級。
另外還有其他類型的Packet,查看同一文件夾發現有shared::Packet/stream::Packet/sync::Packet,他們分別位於shared.rs/stream.rs/sync.rs中。我們重點關注shared::Packet:
pub struct Packet<T> { queue: mpsc::Queue<T>, cnt: AtomicIsize, // How many items are on this channel steals: UnsafeCell<isize>, // How many times has a port received without blocking? to_wake: AtomicUsize, // SignalToken for wake up // The number of channels which are currently using this packet. channels: AtomicUsize, // See the discussion in Port::drop and the channel send methods for what // these are used for port_dropped: AtomicBool, sender_drain: AtomicIsize, // this lock protects various portions of this implementation during // select() select_lock: Mutex<()>, }
清楚地看到queue字段,它用於存放數據。我們先不關注數據字段。
對於這四個類型的Packet,標准庫提供了enun Flavor<T>來做區分:
enum Flavor<T> { Oneshot(Arc<oneshot::Packet<T>>), Stream(Arc<stream::Packet<T>>), Shared(Arc<shared::Packet<T>>), Sync(Arc<sync::Packet<T>>), }
而我們的Sender/Receiver對象則非常簡單地通過存儲Flavor<T>來關聯到Packet:
pub struct Sender<T> { inner: UnsafeCell<Flavor<T>>, } pub struct Receiver<T> { inner: UnsafeCell<Flavor<T>>, }
我們再看一下fn channel:
pub fn channel<T>() -> (Sender<T>, Receiver<T>) { let a = Arc::new(oneshot::Packet::new()); (Sender::new(Flavor::Oneshot(a.clone())), Receiver::new(Flavor::Oneshot(a))) }
就可以了解到Sender/Receiver里面都存了Flavor,根據Flavor的類型區分Packet的類型,同時Packet作為共享數據被安全地共享。
這就是我們調用channel得到的結果。因為我們重點關注多生產者的情況,所以我們再看一下Clone for Sender的實現:
impl<T> Clone for Sender<T> { fn clone(&self) -> Sender<T> { let packet = match *unsafe { self.inner() } { Flavor::Oneshot(ref p) => { let a = Arc::new(shared::Packet::new()); { let guard = a.postinit_lock(); let rx = Receiver::new(Flavor::Shared(a.clone())); let sleeper = match p.upgrade(rx) { oneshot::UpSuccess | oneshot::UpDisconnected => None, oneshot::UpWoke(task) => Some(task), }; a.inherit_blocker(sleeper, guard); } a } Flavor::Stream(ref p) => { let a = Arc::new(shared::Packet::new()); { let guard = a.postinit_lock(); let rx = Receiver::new(Flavor::Shared(a.clone()));