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()这个过程,需要特别小心,否则可能会想象中不一样。这个里面有一些操作系统线程 调度机制比较底层,是比较复杂的。

相关推荐
A懿轩A1 小时前
C/C++ 数据结构与算法【数组】 数组详细解析【日常学习,考研必备】带图+详细代码
c语言·数据结构·c++·学习·考研·算法·数组
古希腊掌管学习的神1 小时前
[搜广推]王树森推荐系统——矩阵补充&最近邻查找
python·算法·机器学习·矩阵
云边有个稻草人1 小时前
【优选算法】—复写零(双指针算法)
笔记·算法·双指针算法
半盏茶香1 小时前
在21世纪的我用C语言探寻世界本质 ——编译和链接(编译环境和运行环境)
c语言·开发语言·c++·算法
忘梓.2 小时前
解锁动态规划的奥秘:从零到精通的创新思维解析(3)
算法·动态规划
Evand J2 小时前
LOS/NLOS环境建模与三维TOA定位,MATLAB仿真程序,可自定义锚点数量和轨迹点长度
开发语言·matlab
LucianaiB2 小时前
探索CSDN博客数据:使用Python爬虫技术
开发语言·爬虫·python
Ronin3052 小时前
11.vector的介绍及模拟实现
开发语言·c++
计算机学长大白3 小时前
C中设计不允许继承的类的实现方法是什么?
c语言·开发语言
PieroPc4 小时前
Python 写的 智慧记 进销存 辅助 程序 导入导出 excel 可打印
开发语言·python·excel