std::mpsc::channel VS tokio::sync::mpsc::channel in Rust
Amit Nadiger
Polyglot(Rust??, Move, C++, C, Kotlin, Java) Blockchain, Polkadot, UTXO, Substrate, Sui, Aptos, Wasm, Proxy-wasm,AndroidTV, Dvb, STB, Linux, Cas, Engineering management.
In Rust, channels provide a way to send messages between threads or tasks, facilitating concurrent and parallel programming. Two primary types of channels as mentioned in the heading are std::sync::mpsc::channel and tokio::sync::mpsc::channel, each designed for different concurrency models: synchronous and asynchronous.
Standard MPSC Channel (std::sync::mpsc::channel)
The std::sync::mpsc::channel is part of Rust’s standard library and is designed for synchronous message passing between threads.
Key Features:
2. Blocking:
3. Concurrency Model:
4. Return Type of recv Method std::sync::mpsc::Receiver::recv:
Simple example :
use std::sync::mpsc;
use std::thread;
fn main() {
let (tx, rx) = mpsc::channel();
thread::spawn(move || {
tx.send("hello").unwrap();
});
while let Ok(msg) = rx.recv() {
println!("Received: {}", msg);
}
}
Tokio MPSC Channel (tokio::sync::mpsc::channel)
The tokio::sync::mpsc::channel is part of the Tokio async runtime and is designed for asynchronous message passing between tasks.
Key Features:
2. Non-blocking:
3. Concurrency Model:
4. Return type of tokio::sync::mpsc::Receiver::recv:
use tokio::sync::mpsc;
#[tokio::main]
async fn main() {
let (tx, mut rx) = mpsc::channel(32);
tokio::spawn(async move {
tx.send("hello").await.unwrap();
});
while let Some(msg) = rx.recv().await {
println!("Received: {}", msg);
}
}
Differences
Return Type:
领英推荐
Mutability:
How can we mix and match both channels and take advantage of both?
use std::{time::Duration, sync::mpsc};
enum Command {
Print(String),
}
#[tokio::main]
async fn main() {
// Spawn a command thread for "heavy lifting"
let (tx, rx) = mpsc::channel::<Command>();
// Spawn a TOKIO Async channel for replies
let (tx_reply, mut rx_reply) = tokio::sync::mpsc::channel::<String>(10);
let handle = tokio::runtime::Handle::current();
std::thread::spawn(move || {
while let Ok(command) = rx.recv() {
match command {
Command::Print(s) => {
println!("Message Reached Thread via std::chnl");
// Make our very own copy of the transmitter
let tx_reply = tx_reply.clone();
handle.spawn(async move {
println!("By-passing msg from tokio task - via tokio::chnl");
tx_reply.send(s).await.unwrap();
// tx_reply.send("Jai".to_string()).await.unwrap();
});
},
}
}
});
// Launch a Tokio process to receive replies from thread-land
tokio::spawn(async move {
while let Some(reply) = rx_reply.recv().await {
println!("Finally got the msg in Tokio via tokio::chnl{}",reply);
}
});
// Launch the async sender
let mut counter = 0;
loop {
tokio::time::sleep(Duration::from_secs(1)).await;
println!("Starting point of msg sent via std::chnl");
tx.send(Command::Print(format!("Msg1 - Jai Shree Ram {counter}"))).unwrap();
counter += 1;
}
}
/*
Starting point of msg sent via std::chnl
Message Reached Thread via std::chnl
By-passing msg from tokio task - via tokio::chnl
Finally got the msg in Tokio via tokio::chnlMsg1 - Jai Shree Ram 0
Starting point of msg sent via std::chnl
Message Reached Thread via std::chnl
By-passing msg from tokio task - via tokio::chnl
Finally got the msg in Tokio via tokio::chnlMsg1 - Jai Shree Ram 1
Starting point of msg sent via std::chnl
Message Reached Thread via std::chnl
By-passing msg from tokio task - via tokio::chnl
Finally got the msg in Tokio via tokio::chnlMsg1 - Jai Shree Ram 2
*/
I referred to this awesome tutorial : Async Channels - KLA Training (Dec 2023) (bracketproductions.com)
What happens if std::sync::mpsc::channel is used for communication between Tokio task ?
We can indeed use both std::sync::mpsc::channel and tokio::sync::mpsc::channel to communicate between Tokio tasks and between threads and vice versa, but each has implications depending on the context in which they're used. Let's look at the impacts and considerations for each:
Using std::sync::mpsc::channel in Tokio
Advantages:
Disadvantages:
2. Inefficiency in Async Context:
Using tokio::sync::mpsc::channel
Advantages:
2. Efficiency:
3. Scalability:
Impact on Communication Between Tokio Tasks and Threads
2. Task Scheduling:
3. Code Complexity:
Conclusion: While we can use std::sync::mpsc::channel in a Tokio-based application, it is generally better to use tokio::sync::mpsc::channel for communication between Tokio tasks due to its non-blocking nature and better integration with the async runtime. This helps maintain the efficiency and performance of your async application. For communication between threads in a synchronous context, std::sync::mpsc::channel remains a good choice. If you need to bridge between synchronous and asynchronous contexts, careful handling is required to avoid blocking the async runtime.