How to create individual thread for sending pings on client webscoket

142 Views Asked by At

I wrote this code, but when sending pings, the program cannot do anything else. How can I spawn another thread to do this work while I do something else in my program?

 pub fn sending_ping(addr: Addr<MicroscopeClient>) -> Result<(), ()> {
    info!("Pings started");

    spawn(async move {
        loop {
            info!("Ping");
            match addr.send(Ping {}).await {
                Ok(_) => {
                    info!("Ping sended")
                }
                Err(e) => {
                    warn!("Ping error");
                    return;
                }
            }
            std::thread::sleep(Duration::from_millis((4000) as u64));
        }
    });
    return Ok(());
}
2

There are 2 best solutions below

0
maks vell On BEST ANSWER

I solved this particular problem in the following way

fn send_heartbeat(&self, ctx: &mut <Self as Actor>::Context) {
    ctx.run_interval(HEARTBEAT_INTERVAL, |act, ctx| {
        if Instant::now().duration_since(act.hb) > CLIENT_TIMEOUT {
            info!("Websocket Client heartbeat failed, disconnecting!");
            act.server_addr.do_send(Disconnect {
                id: act.user_id.clone(),
            });
            ctx.stop();

            return;
        }
    });
}

But I still don't know how to start some long process in a websocket in a separate thread, so that it does not block the websocket, with tokio="0.2.0" and actix="0.3.0".

0
Adam Hitchen On

Assuming you are using tokio, calling tokio::spawn does not necessarily guarantee that the task will be executed on a separate thread (though it may be).

The problem here is that std::thread::sleep() completely blocks the thread that the task is running on, meaning your pings will run asynchronously (not blocking other tasks) but when you move on to the sleep nothing else on the current thread will execute for the next 4 seconds.

You can resolve this by using a non-blocking version of sleep, such as the one provided by tokio https://docs.rs/tokio/latest/tokio/time/fn.sleep.html

Thus your code would look like


 pub fn sending_ping(addr: Addr<MicroscopeClient>) -> Result<(), ()> {
    info!("Pings started");

    spawn(async move {
        loop {
            info!("Ping");
            match addr.send(Ping {}).await {
                Ok(_) => {
                    info!("Ping sended")
                }
                Err(e) => {
                    warn!("Ping error");
                    return;
                }
            }
            tokio::time::sleep(Duration::from_millis(4000)).await;
        }
    });
    return Ok(());
}

If you truly want to ensure that the task spawns on a different thread you would have to use std::thread::spawn but you would then have to set up another async runtime. Instead you could use spawn_blocking which at least guarantees the task is run on a thread where tasks are expected to block