In async STD of rust, it is convenient to use. await to enable asynchronous calls, but task calls use green threads, so recv cannot be used to receive messages from this channel,
Because it will block operating system threads, many green threads will follow and wait.
Here is a small function:
Multithreading writes data to the log module. The log organizes multiple log data blocks into slot slots. After the slots are full, they are written to the disk at one time.
The following code simulates the business process of multithreading data to slot.
The specific code is as follows:
use async_std::task; use std::sync::atomic::{AtomicU64, Ordering}; use std::sync::mpsc::channel; use std::sync::mpsc::Receiver; use std::sync::mpsc::Sender; use std::sync::Arc; const PAGE_SIZE: usize = 4 * 1024; pub struct Slot { joined: Arc<AtomicU64>, buff: Vec<u8>, } impl Slot { #[allow(dead_code)] pub fn new() -> Slot { let buff = vec![0; PAGE_SIZE]; let joined = Arc::new(AtomicU64::new(1)); Slot { joined, buff } } #[allow(dead_code)] pub fn write(&mut self, buff: &[u8]) { let len = buff.len(); self.buff[0..len].copy_from_slice(buff); } } ///Prepare to write, if no new request is received continuously, exit the sub thread async fn do_join(joined: u64, receiver: Receiver<ReqCmd>, sender: Sender<JoinResult>) { println!("do join start!"); let joined_old = joined; let joined = PAGE_SIZE as u64 - joined_old; let mut joined_curr = 0; let mut err_count = 0; while joined_curr < joined { let result = receiver.try_recv(); if result.is_ok() { err_count = 0; let req_cmd = result.unwrap(); println!("receive request command : {:?}", req_cmd); let req_cmd_len = req_cmd.len; if joined_curr + req_cmd_len > joined { // Slot is full and this request is not satisfied println!("Slot It is full and this request is not satisfied"); let join_result = JoinResult::new(0, 0, req_cmd); sender.send(join_result).unwrap(); let exit_result = JoinResult::new_exit(8, joined_curr + joined_old); sender.send(exit_result).unwrap(); return; } // This request has been met. Data can be copied to this slot println!("This request has been met, and data can be copied to this slot"); let join_result = JoinResult::new(1, joined_curr + joined_old, req_cmd); sender.send(join_result).unwrap(); joined_curr = joined_curr + req_cmd_len; if joined_curr == joined { // Just full println!("Just full. slot"); let exit_result = JoinResult::new_exit(8, joined_curr + joined_old); sender.send(exit_result).unwrap(); return; } println!("Not full, continue to receive"); } else { // Not received, exit err_count += 1; println!("The first{}Times not received", err_count); println!("If it is not received, wait for the next reception. If it is not received for 30 consecutive times, exit"); if err_count >= 30 { println!("No command is received for 30 consecutive exits Join Sub thread"); let exit_result = JoinResult::new_exit(9, joined_curr + joined_old); sender.send(exit_result).unwrap(); return; } task::yield_now().await; } } } async fn receive_cmd(receiver: Receiver<JoinResult>) { println!("start receive log request cmd's response"); loop { let result = receiver.try_recv(); if result.is_ok() { let join_result = result.unwrap(); println!("receive log response is: {:?}", join_result); if join_result.kind == 8 || join_result.kind == 9 { println!("join child thread is exited!"); // Sign out return; } } else { println!("receive log response is error"); task::yield_now().await; } } } async fn send_cmd(cmd: ReqCmd, sender: Sender<ReqCmd>) { println!("send log request cmd {:?}", cmd); if sender.send(cmd).is_err() { println!("send log request cmd is err"); } } ///Main calling function pub async fn async_main() { let slot = Slot::new(); // Command to send data arrival let (sender1, receiver1) = channel::<ReqCmd>(); // Used to receive data Join operation results let (sender2, receiver2) = channel::<JoinResult>(); let joined = slot.joined.load(Ordering::Relaxed); let f1 = do_join(joined, receiver1, sender2.clone()); let f2 = receive_cmd(receiver2); let cmd3 = ReqCmd::new(1, 1024); let cmd4 = ReqCmd::new(2, 512); let cmd5 = ReqCmd::new(3, 2048); let cmd6 = ReqCmd::new(4, 1024); let f3 = send_cmd(cmd3, sender1.clone()); let f4 = send_cmd(cmd4, sender1.clone()); let f5 = send_cmd(cmd5, sender1.clone()); let f6 = send_cmd(cmd6, sender1.clone()); let j = async_macros::join!(f3, f4, f1, f2, f5, f6); println!("async_main start task!"); j.await; println!("async_main finished!"); } ///Request command #[derive(Debug, Clone)] struct ReqCmd { log_id: u64, len: u64, } impl ReqCmd { pub fn new(log_id: u64, len: u64) -> ReqCmd { ReqCmd { log_id, len } } } ///If kind == 0, this request command is not allowed to write to this slot ///If kind == 1, it means that this request command is allowed to write to this slot ///If kind == 8, it means that the sub thread will be closed, and the slot is enough, pos means slot.state.joined value; ///If kind == 9, it means that the sub thread will be closed, and the slot is not enough, pos indicates the slot.state.joined value, and exits because the wait timeout; #[derive(Debug, Clone)] struct JoinResult { kind: usize, pos: u64, log_id: u64, len: u64, } impl JoinResult { pub fn new(kind: usize, pos: u64, cmd: ReqCmd) -> JoinResult { let log_id = cmd.log_id; let len = cmd.len; JoinResult { kind, pos, log_id, len, } } pub fn new_exit(kind: usize, pos: u64) -> JoinResult { let log_id = 0; let len = 0; JoinResult { kind, pos, log_id, len, } } }
In the above code, if a loop does not receive a message, call task::yield_now().await; to notify the dispatcher to switch the task to wait.
** Function async_std::task::yield_now **
pub async fn yield_now()
Cooperatively gives up a timeslice to the task scheduler.
Calling this function will move the currently executing future to the back of the execution queue, making room for other futures to execute. This is especially useful after running CPU-intensive operations inside a future.