TL;DR:
mpscstands for multi-producer, single-consumer. It is a channel where many senders can push values and one receiver pulls them. Usestd::sync::mpscfor synchronous/threaded code andtokio::sync::mpscfor async code. Create a channel withmpsc::channel()(unbounded) ormpsc::bounded(n)/mpsc::channel(n)(bounded). Clone theSenderto create additional producers. The channel closes when all senders are dropped.
What Is an mpsc Channel?
An mpsc channel is a queue that lets multiple threads or tasks send values to a single receiver without shared mutable state.
use std::sync::mpsc;
use std::thread;
let (tx, rx) = mpsc::channel();
for i in 0..5 {
let tx = tx.clone();
thread::spawn(move || {
tx.send(i).unwrap();
});
}
drop(tx); // drop the original sender so rx knows when all are done
for val in rx {
println!("received: {val}");
}Cloning tx gives each thread its own sender. The for val in rx loop ends when all senders are dropped.
How Do You Use tokio::sync::mpsc?
For async code, use tokio::sync::mpsc; it provides .send().await and .recv().await that yield instead of blocking.
use tokio::sync::mpsc;
#[tokio::main]
async fn main() {
let (tx, mut rx) = mpsc::channel(32); // bounded: buffer of 32
tokio::spawn(async move {
for i in 0..10 {
tx.send(i).await.unwrap();
}
});
while let Some(val) = rx.recv().await {
println!("received: {val}");
}
}The channel(32) creates a bounded channel. If the buffer fills, .send().await will yield until space is available; applying backpressure automatically.
Bounded vs Unbounded Channels
Bounded channels apply backpressure; unbounded channels can grow without limit and risk out-of-memory.
Bounded (channel(n)) | Unbounded (unbounded_channel()) | |
|---|---|---|
| Buffer | Fixed capacity n | Grows indefinitely |
| Send blocks? | Yes, when full | Never |
| Backpressure | ✅ Automatic | ❌ None |
| Best for | Production services | Burst-tolerant pipelines |
Prefer bounded in production; unbounded channels hide backpressure bugs until you run out of memory under load.
How Does mpsc Differ From Other Channel Types?
mpsc is one-receiver-many-senders. Rust also offers broadcast (many receivers) and oneshot (single value).
| Channel | Senders | Receivers | Use case |
|---|---|---|---|
mpsc | Many | One | Work queues, event aggregation |
broadcast | One | Many | Fan-out notifications |
oneshot | One | One | Single response (request/reply) |
watch | One | Many | Latest-value sharing |
Frequently Asked Questions
tx.send() returns Err(SendError(val)). The value is returned to you so it is not lost.
Technically yes, but .recv() blocks the thread. Use tokio::sync::mpsc in async contexts; it integrates with the async runtime correctly.
No. For mpmc, use crossbeam::channel or flume (async-compatible).
Sources
Related Glossary Terms
- channel: Overview of all channel types in Rust
- tokio: The async runtime for
tokio::sync::mpsc - send-sync: The traits that make channels thread-safe
- select: Often combined with mpsc receivers
- stream: mpsc receivers are often wrapped into streams for ergonomic async pipelines
Keep Reading
- Rust Concurrency with Threads and Channels: mpsc is the foundation of message-passing concurrency in Rust
