Open rkrzr opened 1 year ago
Hello @rkrzr! Rust provides several ways to write concurrent programs. Three commonly used ways are:
Each approach has its own advantages and disadvantages. Using threads with message passing provides good control over resources and is easier to reason about. However, it can be harder to debug and performance may be slower than alternatives.
Using shared memory concurrency with mutexes is faster than using threads with message passing. It works well for programs that need to share data between threads, but can also lead to race conditions and deadlocks that are harder to debug.
Using async/await with Tokio or async-std libraries can be the most efficient way to write concurrent programs, especially those that need to handle a lot of I/O. However, it can be harder to understand and write due to its complexity.
Overall, the choice of approach depends on the specific requirements of the program.
The key thing here is that we can now have a conversation @rkrzr . @robinaidev can decide when it should reply.
@robinaidev can you share some code examples on each of the three commonly used ways?
Sure, here are some code examples for each approach:
use std::thread;
use std::sync::mpsc;
fn main() {
let (sender, receiver) = mpsc::channel();
for i in 0..10 {
let child_sender = sender.clone();
thread::spawn(move || {
child_sender.send(i).unwrap();
});
}
for _ in 0..10 {
println!("{}", receiver.recv().unwrap());
}
}
use std::sync::{Arc, Mutex};
use std::thread;
fn main() {
let counter = Arc::new(Mutex::new(0));
let mut handles = vec![];
for _ in 0..10 {
let counter = Arc::clone(&counter);
let handle = thread::spawn(move || {
let mut num = counter.lock().unwrap();
*num += 1;
});
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
println!("{}", *counter.lock().unwrap());
}
use std::error::Error;
use tokio::io::{self, AsyncReadExt};
use tokio::net::TcpListener;
#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
let listener = TcpListener::bind("127.0.0.1:8080").await?;
loop {
let (mut socket, _) = listener.accept().await?;
tokio::spawn(async move {
let mut buf = [0; 1024];
loop {
let n = match socket.read(&mut buf).await {
Ok(n) if n == 0 => return,
Ok(n) => n,
Err(e) => {
eprintln!("failed to read from socket; err = {:?}", e);
return;
}
};
if let Err(e) = socket.write_all(&buf[0..n]).await {
eprintln!("failed to write to socket; err = {:?}", e);
return;
}
}
});
}
}
I hope these code examples help you understand the three approaches better!
Hey @robinaidev, what are the top 3 ways to write a concurrent program in Rust? Please elaborate on the pros and cons of each approach and compare them to each other.