How to mutate self within a thread? [duplicate] - multithreading

This question already has answers here:
How do I share a mutable object between threads using Arc?
(1 answer)
Sharing mutable self between multiple threads
(1 answer)
Closed 4 years ago.
I am writing an application which should read from a serial port in a loop (like a watcher) and also write commands to it.
The main thread is allowed to only write while the created thread can only read.
I created a simple example to reproduce my problem here. tx_thread reads from the serial port in a loop and on a certain condition it sends a message via MPSC channel. rx_thread looks for messages; when there is anything available it processes and it should also mutate the current state of the struct.
use std::sync::mpsc;
use std::thread;
use std::time::Duration;
// this will also implement Drop trait to wait threads to
// be finished (message will be Enum instead of number in this case)
#[derive(Debug)]
struct MyStruct {
num: u32,
tx_thread: Option<thread::JoinHandle<()>>,
rx_thread: Option<thread::JoinHandle<()>>,
}
impl MyStruct {
fn new() -> MyStruct {
MyStruct {
num: 0,
tx_thread: None,
rx_thread: None,
}
}
fn start(&mut self) {
let (tx, rx) = mpsc::channel();
// tx thread will read from serial port infinitely,
// and send data to mpsc channel after certain condition
// to be processed.
let tx_thread = thread::spawn(move || {
let mut i = 0;
loop {
tx.send(i).unwrap();
i += 1;
thread::sleep(Duration::from_secs(1));
}
});
// after this will receive message, it will start
// processing and mutate `self` state if needed.
let rx_thread = thread::spawn(move || loop {
let num = rx.recv().unwrap();
println!("{:?}", num);
/* here, how do I save `num` to `self`? */
thread::sleep(Duration::from_secs(1));
});
self.tx_thread = Some(tx_thread);
self.rx_thread = Some(rx_thread);
}
}
fn main() {
let mut s = MyStruct::new();
s.start();
thread::sleep(Duration::from_secs(999999));
}

One amazing guy (Broken pen) on discord channel told me pretty much great solution to this, all credits to him.
So solution is to put properties that we want to be mutated in Arc<Mutex<>> instead and move cloned reference into the thread. So basically code will look like this:
use std::sync::{mpsc, Arc, Mutex};
use std::thread;
use std::time::Duration;
type MyType = Arc<Mutex<u32>>;
#[derive(Debug)]
struct MyStruct {
num: MyType,
tx_thread: Option<thread::JoinHandle<()>>,
rx_thread: Option<thread::JoinHandle<()>>,
}
impl MyStruct {
fn new() -> MyStruct {
MyStruct {
num: Arc::new(Mutex::new(0)),
tx_thread: None,
rx_thread: None,
}
}
fn start(&mut self) {
let (tx, rx) = mpsc::channel();
// tx thread will read from serial port infinitely,
// and send data to mpsc channel after certain condition
// to be processed.
let tx_thread = thread::spawn(move || {
let mut i = 0;
loop {
tx.send(i).unwrap();
i += 1;
thread::sleep(Duration::from_secs(1));
}
});
// clone here.
let arc_num = self.num.clone();
let rx_thread = thread::spawn(move || loop {
let num = rx.recv().unwrap();
// println!("{:?}", num);
// now we can use it for writing/reading.
*arc_num.lock().unwrap() = num;
println!("{:?}", *arc_num.lock().unwrap());
thread::sleep(Duration::from_secs(1));
});
self.tx_thread = Some(tx_thread);
self.rx_thread = Some(rx_thread);
}
}
fn main() {
let mut s = MyStruct::new();
s.start();
thread::sleep(Duration::from_secs(999999));
}
EDIT: another solution is to create inner struct with Arc<Mutex<>> and do work there, which gives you access to everything you need.
See code below:
use std::default::Default;
use std::sync::{mpsc, Arc, Mutex};
use std::thread;
use std::time::Duration;
// this will also implement Drop trait to wait threads to
// be finished (message will be Enum instead of number in this case)
#[derive(Debug, Default)]
struct MyStructInner {
num: u32,
tx_thread: Option<thread::JoinHandle<()>>,
rx_thread: Option<thread::JoinHandle<()>>,
}
#[derive(Debug, Default)]
struct MyStruct {
inner: Arc<Mutex<MyStructInner>>,
}
impl MyStruct {
fn new() -> MyStruct {
MyStruct {
inner: Arc::new(Mutex::new(MyStructInner {
num: 0,
..Default::default()
})),
}
}
fn start(&mut self) {
let (tx, rx) = mpsc::channel();
// tx thread will read from serial port infinitely,
// and send data to mpsc channel after certain condition
// to be processed.
let tx_thread = thread::spawn(move || {
let mut i = 0;
loop {
tx.send(i).unwrap();
i += 1;
thread::sleep(Duration::from_secs(1));
}
});
// after this will receive message, it will start
// processing and mutate `self` state if needed.
let local_self = self.inner.clone();
let rx_thread = thread::spawn(move || loop {
let num = rx.recv().unwrap();
local_self.lock().unwrap().num = num;
println!("{:?}", local_self.lock().unwrap().num);
thread::sleep(Duration::from_secs(1));
});
self.inner.lock().unwrap().tx_thread = Some(tx_thread);
self.inner.lock().unwrap().rx_thread = Some(rx_thread);
}
}
fn main() {
let mut s = MyStruct::new();
s.start();
thread::sleep(Duration::from_secs(999999));
}

Related

what is the best design for Event base code in both write and read?

I have an Event design that on receiving data a notify call back will be called. but the thing is when I used it in a multi thread system and put variables in Arc<Mutex> the lock stucks on response. I am looking for solutions. I think splitting the send and receive part is one of the solutions. here is the code:
use spin::Mutex;
use std::sync::Arc;
trait Dd{
fn notify(&mut self);
}
struct a {
write: Vec<Arc<Mutex<dyn Dd>>>// spin::Mutex
}
impl Dd for a{
fn notify(&mut self){
for notif in &mut self.write{
(*notif.lock()).notify();// spin::Mutex
}
}
}
struct conn{
should_notify: Vec<Arc<Mutex<dyn Dd>>>// spin::Mutex
}
impl Dd for conn{
fn notify(&mut self){
for notif in &mut self.should_notify{
(*notif.lock()).notify();// spin::Mutex
}
}
}
unsafe impl Send for conn {}
unsafe impl Sync for conn {}
fn main() {
let mut a = a{write: Vec::new()};
let mut con = conn{should_notify: Vec::new()};
let arc_a = Arc::from(Mutex::from(a));
con.should_notify.push(arc_a.clone());
let mut conn = Arc::from(Mutex::from(con));
{
(*arc_a.lock()).write.push(conn.clone());
}
let conn1 = conn.clone();
let handle = std::thread::spawn(move ||{
for i in 0..100{
(*conn1.lock()).notify();
println!("hiiiiii");
std::thread::sleep(std::time::Duration::from_millis(10));
}
});
handle.join();
}
Playground

Rust send serialized struct from crossbeam channel to multiple receivers via tcp

I am trying to send a serialized struct over tcp to multiple machines. The tcp handler receives the serialized struct (String type) by a crossbeam channel from another thread.
My problem is that the rx.try_iter() will drain the crossbeam channel, and if more than one client is connected the clients can't receive the same struct. I tried moving the rx.try_iter() out of the individual handle_client() function, but couldn't achieve a good result.
Thanks for your time and help!
This is what I have so far:
(Server side)
use std::net::{TcpListener, TcpStream};
use std::thread;
use std::io::{Read,Write,Error};
use serde::{Serialize, Deserialize};
use crossbeam_channel::unbounded;
#[derive(Serialize, Deserialize)]
pub struct Serialized {
pub buffer: Vec<u32>,
pub timestep: u128,
}
impl Serialized {
pub fn serialize(buffer: Vec<u32>, timestep: u128) -> String {
let x = Serialized {
buffer,
timestep
};
serde_json::to_string(&x).unwrap()
}
}
fn handle_client(mut stream: TcpStream, rx: crossbeam_channel::Receiver<String>)-> Result<(), Error> {
println!("incoming connection from: {}", stream.peer_addr()?);
loop {
//receive from channel
let serialized = match rx.try_iter().last(){
Some(x) => x,
None => continue,
};
//write to stream
stream.write(serialized.as_bytes())?;
}
}
pub fn start_server(rx: crossbeam_channel::Receiver<String>) {
let listener = TcpListener::bind("localhost:8888").expect("Could not bind");
for stream in listener.incoming() {
let rx = rx.clone();
match stream {
Err(e)=> {eprintln!("failed: {}", e)}
Ok(stream) => {
thread::spawn(move || {
handle_client(stream, rx).unwrap_or_else(|error| eprintln!("{:?}", error));
});
}
}
}
}
(Client side)
use std::net::TcpStream;
use serde::{Serialize, Deserialize};
use std::error::Error as er;
#[derive(Serialize, Deserialize, Debug)]
pub struct Serialized {
pub buffer: Vec<u32>,
pub timestep: u128,
}
fn read_user_from_stream(tcp_stream: &mut TcpStream) -> Result<Serialized, Box<dyn er>> {
let mut de = serde_json::Deserializer::from_reader(tcp_stream);
let u = Serialized::deserialize(&mut de)?;
Ok(u)
}
pub fn start_client() {
loop {
let mut stream = TcpStream::connect("localhost:8888").expect("could not connect");
let serialized = read_user_from_stream(&mut stream).unwrap();
println!("timestep: {}", serialized.timestep);
}
}
fn main() {
start_client();
}
You can't use crossbeam to broadcast items. crossbeam only provides a producer-consumer architecture; if you want to deliver an item to multiple receivers, you need to use a different mechanism.
It seems that the bus crate provides what you need.
After some discussion on the rust user board, I came to this solution (server side):
use std::sync::{Arc, Mutex};
use std::thread;
use std::net::{TcpListener, TcpStream};
use std::io::{Read,Write,Error};
use bus::{Bus, BusReader};
fn main() {
let mut x: u32 = 0;
let bus = Bus::<u32>::new(10);
let bus_mutex = Arc::new(Mutex::new(bus));
let bus_mutex_cp = Arc::clone(&bus_mutex);
thread::spawn(move || {
start_server(bus_mutex_cp);
});
//simulation loop
for _ in 0..99999 {
x = x + 1;
println!("Simulation step: {}", x);
bus_mutex.lock().unwrap().broadcast(x);
thread::sleep_ms(1000);
}
loop {}
}
pub fn start_server(bus_mutex: Arc<Mutex<Bus<u32>>>) {
let listener = TcpListener::bind("0.0.0.0:8888").expect("Could not bind");
for stream in listener.incoming() {
match stream {
Err(e)=> {eprintln!("failed: {}", e)}
Ok(stream) => {
let rx = bus_mutex.lock().unwrap().add_rx();
thread::spawn(move || {
handle_client(stream, rx).unwrap_or_else(|error| eprintln!("{:?}", error));
});
}
}
}
}
fn handle_client(mut stream: TcpStream, mut rx: BusReader<u32>)-> Result<(), Error> {
println!("incoming connection from: {}", stream.peer_addr()?);
loop {
//receive from bus
let x = rx.recv().unwrap();
//write to stream
stream.write(&x.to_string().as_bytes())?;
thread::sleep_ms(100);
}
}

Reference to a TCPListener and TcpStream from one thread to a Struct in another thread

The current issue that I am having is creating a way to reference the TCPListener from a thread into the Struct of the main thread. The end goal of what I am trying to accomplish is a way to reference the server's TCP connections from the Server struct.
Here is the code:
use std::io::Read;
use std::net::{TcpListener, TcpStream};
use std::sync::{Arc, mpsc, Mutex};
use std::thread;
fn main() {
let server = Server::start("127.0.0.1:25565".to_string());
loop {
for client in server.connected_clients {
println!("{:?}", client.stream.peer_addr().unwrap())
}
}
}
#[derive(Debug)]
struct Server {
listener: Arc<Mutex<TcpListener>>,
connected_clients: Vec<Client>,
}
impl Server {
pub fn start(address: String) -> Server {
let listener = TcpListener::bind(address).unwrap();
let (tx, rx) = mpsc::channel();
let listener = Arc::new(Mutex::new(listener));
let server = Server {
listener: listener,
connected_clients: Vec::new()
};
tx.send(&server.listener).unwrap();
thread::spawn(|| {
let listener = rx.recv().unwrap();
// For each new connection start a new thread
for stream in listener.lock().unwrap().incoming() {
let mut stream = stream.unwrap();
thread::spawn(move || {
// TODO: Add client to the connected_clients Vec
let mut buffer = [0; 1024];
loop {
stream.read(&mut buffer).unwrap();
println!("{}", String::from_utf8(Vec::from(&buffer[..])).unwrap().trim_end_matches(char::from(0)));
}
});
}
});
server
}
}
#[derive(Debug)]
struct Client {
id: usize,
stream: TcpStream,
}
ERROR: std::sync::mpsc::Receiver<&Arc<Mutex>>` cannot be shared between threads safely
I'm uncertain where you got the idea from to use channel to send an object to a thread you just spawned, but it's definitely incorrect in this case.
Just use a move || closure instead to move outer variables into it.
With that in mind, this code compiles: (although I have no way to test if it works)
use std::io::Read;
use std::net::{TcpListener, TcpStream};
use std::sync::{mpsc, Arc, Mutex};
use std::thread;
fn main() {
let server = Server::start("127.0.0.1:25565".to_string());
loop {
for client in &server.connected_clients {
println!("{:?}", client.stream.peer_addr().unwrap())
}
}
}
#[derive(Debug)]
struct Server {
listener: Arc<Mutex<TcpListener>>,
connected_clients: Vec<Client>,
}
impl Server {
pub fn start(address: String) -> Server {
let server = Server {
listener: Arc::new(Mutex::new(TcpListener::bind(address).unwrap())),
connected_clients: Vec::new(),
};
let listener = server.listener.clone();
thread::spawn(move || {
// For each new connection start a new thread
for stream in listener.lock().unwrap().incoming() {
let mut stream = stream.unwrap();
thread::spawn(move || {
// TODO: Add client to the connected_clients Vec
let mut buffer = [0; 1024];
loop {
stream.read(&mut buffer).unwrap();
println!(
"{}",
String::from_utf8(Vec::from(&buffer[..]))
.unwrap()
.trim_end_matches(char::from(0))
);
}
});
}
});
server
}
}
#[derive(Debug)]
struct Client {
id: usize,
stream: TcpStream,
}
Technically, while being complete overkill in this case, your channel approach would work as well, but then you have to send an actual instance of the object instead of a reference. And you still need to use a move || closure to move the rx object into it.
use std::io::Read;
use std::net::{TcpListener, TcpStream};
use std::sync::{mpsc, Arc, Mutex};
use std::thread;
fn main() {
let server = Server::start("127.0.0.1:25565".to_string());
loop {
for client in &server.connected_clients {
println!("{:?}", client.stream.peer_addr().unwrap())
}
}
}
#[derive(Debug)]
struct Server {
listener: Arc<Mutex<TcpListener>>,
connected_clients: Vec<Client>,
}
impl Server {
pub fn start(address: String) -> Server {
let listener = TcpListener::bind(address).unwrap();
let (tx, rx) = mpsc::channel();
let listener = Arc::new(Mutex::new(listener));
let server = Server {
listener: listener,
connected_clients: Vec::new(),
};
tx.send(server.listener.clone()).unwrap();
thread::spawn(move || {
let listener = rx.recv().unwrap();
// For each new connection start a new thread
for stream in listener.lock().unwrap().incoming() {
let mut stream = stream.unwrap();
thread::spawn(move || {
// TODO: Add client to the connected_clients Vec
let mut buffer = [0; 1024];
loop {
stream.read(&mut buffer).unwrap();
println!(
"{}",
String::from_utf8(Vec::from(&buffer[..]))
.unwrap()
.trim_end_matches(char::from(0))
);
}
});
}
});
server
}
}
#[derive(Debug)]
struct Client {
id: usize,
stream: TcpStream,
}
One last remark:
I question the usefulness of the entire construct, though. As soon as your thread is inside the for loop, your listener is continuously locked, meaning, whoever tries to actually access it through your Server object will simply deadlock.

add lifetime for a struct that invoke method in multi-threading environment

This is my program:
use std::sync::{Arc, Mutex, MutexGuard};
use std::thread;
trait Animal: Send + Sync { fn get_id(&self) -> i32; }
struct Cat {}
impl Animal for Cat {
fn get_id(&self) -> i32 { return 0; }
}
struct Thread {
id: i32,
ptr: Arc<dyn Animal>,
}
impl Thread {
pub fn multi_threading(&self) {
let shared_array = Arc::new(Mutex::new([0; 5]));
let mut handles = vec![];
for _ in 0..5 {
let array_ptr = Arc::clone(&shared_array);
let handle = thread::spawn(move ||
self.assign(&mut array_ptr.lock().unwrap())
);
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
}
pub fn assign(&self, array: &mut MutexGuard<[i32; 5]>) {
array[self.id as usize] = self.id * self.id + self.ptr.get_id();
}
}
unsafe impl Send for Thread {}
fn main() {
let cat = Cat {};
let ptr_cat = Arc::new(cat);
let thread = Thread { id: 0, ptr: ptr_cat.clone() };
thread.multi_threading();
}
struct Thread is defined with a pointer to a trait object, its member method multi_threading does nothing but assigning value to an array that can be accessed by several threads.
When I compile the program, the error says the &self from pub fn multi_threading(&self)
has an anonymous lifetime '_ but it needs to satisfy a 'static lifetime requirement
Now where should I add this 'static lifetime to satisfy the requirement, to get the program complied?
You can wrap your instance in an Arc itself. That way you can send it to your threads:
impl Thread {
pub fn multi_threading(self: &Arc<Self>) {
let shared_array = Arc::new(Mutex::new([0; 5]));
let mut handles = vec![];
for _ in 0..5 {
let array_ptr = Arc::clone(&shared_array);
let s = self.clone();
let handle = thread::spawn(move ||
s.assign(&mut array_ptr.lock().unwrap())
);
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
}
pub fn assign(&self, array: &mut MutexGuard<[i32; 5]>) {
array[self.id as usize] = self.id * self.id + self.ptr.get_id();
}
}
...
fn main() {
let cat = Cat {};
let ptr_cat = Arc::new(cat);
let thread = Arc::new(Thread { id: 0, ptr: ptr_cat.clone() });
thread.multi_threading();
}
Playground
Notice that you would not need unsafe impl Send for Thread {}, because with Arc it is safe to share it.

"the type does not fulfill the required lifetime" when using a method in a thread

I am trying to use a method in a thread in Rust, but I get the following error message
:21:10: 21:23 error: the type [closure#<anon>:21:24: 23:14
tx:std::sync::mpsc::Sender<i32>, self:&MyStruct, adder:i32, a:i32]
does not fulfill the required lifetime :21
thread::spawn(move || {
^~~~~~~~~~~~~ :18:9: 24:10 note: in this expansion of for loop expansion note: type must outlive the static
lifetime error: aborting due to previous error
This is the example code:
use std::thread;
use std::sync::mpsc;
struct MyStruct {
field: i32
}
impl MyStruct {
fn my_fn(&self, adder1: i32, adder2: i32) -> i32 {
self.field + adder1 + adder2
}
fn threade_test(&self) {
let (tx, rx) = mpsc::channel();
let adder = 1;
let lst_adder = vec!(2, 2, 2);
for a in lst_adder {
let tx = tx.clone();
thread::spawn(move || {
let _ = tx.send(self.my_fn(adder, a));
});
}
println!("{}", rx.recv().unwrap());
}
}
fn main() {
let ms = MyStruct{field: 42};
ms.threade_test();
}
Test it on the Rust Playground.
The problem is that every variable moved to the thread must have the lifetime 'static. i.e. threads can't reference values which are not owned by the thread.
In this case the problem is that self is a reference to an instance of MyStruct.
To solve it, remove every reference and clone the structure before sending it to the thread.
use std::thread;
use std::sync::mpsc;
#[derive(Clone)]
struct MyStruct {
field: i32
}
impl MyStruct {
fn my_fn(&self, adder1: i32, adder2: i32) -> i32 {
self.field + adder1 + adder2
}
fn threade_test(&self) {
let (tx, rx) = mpsc::channel();
let adder = 1;
let lst_adder = vec!(2, 2, 2);
for a in lst_adder {
let tx = tx.clone();
let self_clone = self.clone();
thread::spawn(move || {
let _ = tx.send(self_clone.my_fn(adder, a));
});
}
println!("{}", rx.recv().unwrap());
}
}
fn main() {
let ms = MyStruct{field: 42};
ms.threade_test();
}

Resources