bigywater.tech
Rust · 89

mpsc 多生产者单消费者

mpsc 的名字就是「多生产者、单消费者」。这篇验证多生产者之间的顺序保证、单消费者的限制与变通、忘记 drop 发送端的经典死等,以及有界通道的阻塞与吞吐量差别。

保序
同一个生产者的消息到达顺序不变(不同生产者交错)
204×
sync_channel(1) 比无界 channel 慢的倍数(100 万条)
E0599
Receiver 调用 clone

多个生产者

实测 · 本机执行输出
  编译:成功
  运行(退出码 0):
    共收到 12 条
    生产者 0 的序号按发送顺序到达: true
    生产者 1 的序号按发送顺序到达: true
    生产者 2 的序号按发送顺序到达: true
    到达顺序(生产者编号): 000012111222

通道的基本语义见Channel 消息传递。

忘记 drop 发送端

实测 · 本机执行输出
  编译:成功
  运行(退出码 0):
    Ok(1)
    子线程已结束(它的 tx2 已 drop),但主线程手里的 tx 还活着:Err(Timeout)
    drop(tx) 之后:Err(Disconnected)

单消费者的限制

实测 · 本机执行输出
  源码:
      1 | use std::sync::mpsc;
      2 | fn main() { let (_tx, rx) = mpsc::channel::<i32>(); let _r2 = rx.clone(); }
  编译:失败
    error[E0599]: no method named `clone` found for struct `std::sync::mpsc::Receiver<T>` in the current scope
     --> <实验目录>/src/u89c.rs:2:66
      |
    2 | fn main() { let (_tx, rx) = mpsc::channel::<i32>(); let _r2 = rx.clone(); }
      |                                                                  ^^^^^ method not found in `std::sync::mpsc::Receiver<i32>`
    
    error: aborting due to 1 previous error
    
    For more information about this error, try `rustc --explain E0599`.
  源码:
      1 | use std::{sync::mpsc, thread};
      2 | fn main() { let (_tx, rx) = mpsc::channel::<i32>(); thread::scope(|s| { s.spawn(|| { let _ = rx.recv(); }); s.spawn(|| { let _ = rx.recv(); }); }); }
  编译:失败
    error[E0277]: `std::sync::mpsc::Receiver<i32>` cannot be shared between threads safely
       --> <实验目录>/src/u89d.rs:2:81
        |
      2 | ...ope(|s| { s.spawn(|| { let _ = rx.recv(); }); s.spawn(|| { let _ = rx.recv(); }); }); }
        |                ----- ^^^^^^^^^^^^^^^^^^^^^^^^^ `std::sync::mpsc::Receiver<i32>` cannot be shared between threads safely
        |                |
        |                required by a bound introduced by this call
        |
        = help: the trait `Sync` is not implemented for `std::sync::mpsc::Receiver<i32>`
        = note: required for `&std::sync::mpsc::Receiver<i32>` to implement `Send`
    note: required because it's used within this closure
       --> <实验目录>/src/u89d.rs:2:81
        |
      2 | fn main() { let (_tx, rx) = mpsc::channel::<i32>(); thread::scope(|s| { s.spawn(|| { let _ = rx.recv(); }); s.spawn(|| { let _ = rx...
        |                                                                                 ^^
    note: required by a bound in `Scope::<'scope, 'env>::spawn`
       --> <rust 标准库源码>/library/std/src/thread/scoped.rs:203:28
        |
    201 |     pub fn spawn<F, T>(&'scope self, f: F) -> ScopedJoinHandle<'scope, T>
        |            ----- required by a bound in this associated function
    202 |     where
    203 |         F: FnOnce() -> T + Send + 'scope,
        |                            ^^^^ required by this bound in `Scope::<'scope, 'env>::spawn`
    
    error: aborting due to 1 previous error
    
    For more information about this error, try `rustc --explain E0277`.

Sync 的含义见Send 与 Sync。

多消费者的变通

实测 · 本机执行输出
  编译:成功
  运行(退出码 0):
    处理了 20 个任务,每个任务恰好一次: true
    4 个工作线程各处理的任务数: [5, 5, 5, 5]

有界通道

实测 · 本机执行输出
  编译:成功
  运行(退出码 0):
    容量 2:已发 2 个,try_send 第 3 个: Err(TrySendError::Full(..))
    接收端睡了 100 ms 才取走一个:Ok(1)
    第 3 个 send 在通道满时被阻塞了约 100 毫秒(取整到 50)
    容量 0(会合):没有接收者在等时 try_send = Err(TrySendError::Full(..))
    接收者在等时 send 成功,对方收到 7

吞吐量

实测 · 本机执行输出
  编译:成功
  运行(退出码 0):
    unbounded  100 万条     11.7 ms(约     12 ns/条)
    sync1      100 万条   2384.3 ms(约   2384 ns/条)
    sync1024   100 万条      7.6 ms(约      8 ns/条)

数字是本机 release 实测的最小值,只测了 1 个生产者 1 个消费者;更多线程时的竞争没有测。

分步演示

mpsc
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / rustc 1.98.1(brew)/ cargo 1.98.1;单个文件用 rustc 直接编译,需要依赖时用 cargo(经代理从 crates.io 下载);计时用 std::time::Instant,数字每次运行会有小幅抖动
    • 没有验证:crossbeam-channel / flume 的性能与功能对比、多个生产者同时竞争时的吞吐量、通道里消息很大时的内存、sync_channel 的公平性、async 通道(见异步部分)
    京ICP备2026067502号-1京公网安备11010502063487号