rust : condvar中一对一和多对一模式初探

condvar是不经常碰到的,但其实在tokio之类库中,还是非常核心的作用。

想进一步体会condvar的使用,还是从场景出发。

一、一个通知发送者,一个接收者

假定一个员工收到一个任务,就是模拟是一个时间片,到时了,就会触发通知发出(notify_one)。

通知也有不断发出通知,还是事件触发后再发出通知。这些根据场景需要都可以。

不管形式如何,但mutex中data值的不同状态,wait判断事件是否已经发生的标志。

复制代码
use std::sync::{ Arc, Condvar, Mutex };
use std::thread;
use std::time::{Duration,Instant};
use std::collections::HashMap;
fn main_single() {
    let builder = thread::Builder::new();
    let pair = Arc::new((Mutex::new(false), Condvar::new())); // data为bool值,以此判断事件是否发出
    let pair2 = Arc::clone(&pair);
    //单个通知者
    let handle = builder.spawn(move || {
        let start = Instant::now();
        let mut num_send = 0;
        let mut is_ok =false;
        while !is_ok {
            thread::sleep(Duration::from_secs(1));
            let (lock, cvar) = &*pair2;
            let mut started = lock.lock().unwrap();
            num_send +=1;
            println!("员工:第 {:?} 次报告;任务执行中.....,已耗时: {:?} 秒",num_send, start.elapsed().as_secs());
            cvar.notify_one();
            let is_event_happened = start.elapsed().as_secs() >6;
            if is_event_happened{
                println!("员工: 报告boss,任务已经完成了!共耗时: {:?} 秒",start.elapsed().as_secs());
                *started = true;
                //cvar.notify_all();
                is_ok = true;
            }
        }
    });
    let mut is_received = false;
    let seconds = 1;
    let start = Instant::now();
    let mut num_receive = 0;
    while !is_received {
        let (lock, cvar) = &*pair;
        let mut started = lock.lock().unwrap();
        
        started = cvar.wait(started).unwrap();
        if *started == false{
            println!("boss: 时间过去 {:?} 秒,任务还没完成!",start.elapsed().as_secs());
        }else {
            println!("boss: 好!收到员工任务完成信息!现在过去了 {:?} 秒!",start.elapsed().as_secs());
            is_received = true;
        }
        num_receive +=1;
        println!("boss: 收到! 收到{:?} 次报告,共过去了: {:?} 秒",num_receive, start.elapsed().as_secs());
        thread::sleep(Duration::from_secs(seconds));
    }
    if is_received{
        println!("任务完成!");
    }else{
        println!("任务失败!");
    }
    handle.expect("任务失败!boss要崩溃了!").join().unwrap();
    
}
fn main(){
	main_single()
}

这种场景比较简单,是一对一的。但其它典型的场景可能还有多对一,不断发送通知给一个接收者。象N个员工和一个上级。

二、多个通知者,一个接收者

通过Mutex中包裹一个Hashmap结构,来对多个通知的状态进行管理。

下例构造了3个员工汇报对应一个老板接收的方式。

mutex中值的结构是hashmap<usize,(nums,bool)>结构,分别指的是员工id编号usize,以及此员工通知发送次数nums,以及事件是否发生标志状态bool。

这个数据结构根据自己需要来设定,这里只是一个假设了一个相对信息量复杂的场景。

复制代码
use std::sync::{ Arc, Condvar, Mutex };
use std::thread;
use std::time::{Duration,Instant};
use std::collections::HashMap;
fn main_multi(){

    let pair = Arc::new((Mutex::new(HashMap::new()), Condvar::new()));
    let mut handles  = Vec::new();
    const PERSONS :usize = 3;//多个成员向1个
    let each_send_seconds = 1;
    for i in 0..PERSONS {
        let _pair = Arc::clone(&pair);
        let handle = thread::spawn(move || {
            let start = Instant::now();
            let mut num_send = 0;
            let mut is_ok = false;
            while !is_ok {
                thread::sleep(Duration::from_secs(each_send_seconds));
                let (lock, cvar) = &*_pair;
                let mut locked = lock.lock().unwrap();
                num_send +=1;
                let is_event_happened = start.elapsed().as_secs() >= 6;//定义为事件
                if is_event_happened{
                    println!("员工: 我是员工{:?} ,报告boss,我任务执行完成!共耗时: {:?} 秒",i,start.elapsed().as_secs());
                    (*locked).insert(i,(num_send,true));
                    is_ok = true;
                }else{
                    println!("员工:我是员工{:?} ,第 {:?} 次报告,任务执行中.....,已耗时:: {:?} 秒",i,num_send, start.elapsed().as_secs());
                    (*locked).insert(i,(num_send,false));
                }
                cvar.notify_one();
            }
        });
        handles.push(handle);
    }

    let mut is_all_received = false;
    //let each_recv_seconds = 1;
    let start = Instant::now();
    //let mut num_receive = 0;
    while !is_all_received {
        let mut is_status = vec![false;PERSONS];
        //每次轮询全部一次        
        let (lock, cvar) = &*pair;
        let mut locked = lock.lock().unwrap();
        locked = cvar.wait(locked).unwrap();
        for i in 0..PERSONS{
            if (*locked).contains_key(&i){
                let num = (*locked).get(&i).unwrap();
                let status = num.1;
                if status{
                    println!("员工:{:?}, 通知次数: {:?} status : {:?}",i,num.0,status);
                    is_status[i] = true;
                }
            }
        }
        if is_status.iter().all(|&i| i == true){
            println!("boss: 很好,收到全部员工任务完成的信息!现在过去了 {:?} 秒!",start.elapsed().as_secs());
            is_all_received = true;
        }

    }
    if is_all_received {
        println!("任务完成!");
    }else{
        println!("任务失败!");
    }
    println!("任务总共花了: {:?} 秒", start.elapsed().as_secs());
    handles.into_iter().for_each(|w| w.join().unwrap());
}

fn main(){
    //main_single();
    main_multi();
}

运行结果:

复制代码
员工:我是员工2 ,第 1 次报告,任务执行中.....,已耗时:: 1 秒
员工:我是员工1 ,第 1 次报告,任务执行中.....,已耗时:: 1 秒
员工:我是员工0 ,第 1 次报告,任务执行中.....,已耗时:: 1 秒
员工:我是员工0 ,第 2 次报告,任务执行中.....,已耗时:: 2 秒
员工:我是员工1 ,第 2 次报告,任务执行中.....,已耗时:: 2 秒
员工:我是员工2 ,第 2 次报告,任务执行中.....,已耗时:: 2 秒
员工:我是员工1 ,第 3 次报告,任务执行中.....,已耗时:: 3 秒
员工:我是员工0 ,第 3 次报告,任务执行中.....,已耗时:: 3 秒
员工:我是员工2 ,第 3 次报告,任务执行中.....,已耗时:: 3 秒
员工:我是员工0 ,第 4 次报告,任务执行中.....,已耗时:: 4 秒
员工:我是员工1 ,第 4 次报告,任务执行中.....,已耗时:: 4 秒
员工:我是员工2 ,第 4 次报告,任务执行中.....,已耗时:: 4 秒
员工:我是员工2 ,第 5 次报告,任务执行中.....,已耗时:: 5 秒
员工:我是员工0 ,第 5 次报告,任务执行中.....,已耗时:: 5 秒
员工:我是员工1 ,第 5 次报告,任务执行中.....,已耗时:: 5 秒
员工: 我是员工2 ,报告boss,我任务执行完成!共耗时: 6 秒
员工:2, 通知次数: 6 status : true
员工: 我是员工1 ,报告boss,我任务执行完成!共耗时: 6 秒
员工: 我是员工0 ,报告boss,我任务执行完成!共耗时: 6 秒
员工:0, 通知次数: 6 status : true
员工:1, 通知次数: 6 status : true
员工:2, 通知次数: 6 status : true
boss: 很好,收到全部员工任务完成的信息!现在过去了 6 秒!
任务完成!
任务总共花了: 6 秒

三、多对多模式

在多对一的基础上,还可以衍生中多对多的模式。这个可以扩展一下。也可用到noftify_all。

四、其它相关问题

要注意notify_one()到wait()这个过程,需要特别小心,否则可能会想象中不一样。这个里面有一些操作系统线程 调度机制比较底层,是比较复杂的。

相关推荐
0白露1 小时前
Apifox Helper 与 Swagger3 区别
开发语言
Tanecious.2 小时前
机器视觉--python基础语法
开发语言·python
叠叠乐2 小时前
rust Send Sync 以及对象安全和对象不安全
开发语言·安全·rust
想跑步的小弱鸡2 小时前
Leetcode hot 100(day 3)
算法·leetcode·职场和发展
niandb3 小时前
The Rust Programming Language 学习 (九)
windows·rust
Tttian6223 小时前
Python办公自动化(3)对Excel的操作
开发语言·python·excel
xyliiiiiL3 小时前
ZGC初步了解
java·jvm·算法
爱的叹息4 小时前
RedisTemplate 的 6 个可配置序列化器属性对比
算法·哈希算法
独好紫罗兰4 小时前
洛谷题单2-P5713 【深基3.例5】洛谷团队系统-python-流程图重构
开发语言·python·算法
每次的天空5 小时前
Android学习总结之算法篇四(字符串)
android·学习·算法