首页 > 其他分享 >文盘Rust -- Mutex解决并发写文件乱序问题

文盘Rust -- Mutex解决并发写文件乱序问题

时间:2023-08-11 10:12:26浏览次数:38  
标签:-- 文盘 write let file 1691288040 375 374 乱序

在实际开发过程中,我们可能会遇到并发写文件的场景,如果处理不当很可能出现文件内容乱序问题。下面我们通过一个示例程序描述这一过程并给出解决该问题的方法。

use std::{
    fs::{self, File, OpenOptions},
    io::{Write},
    sync::Arc,
    time::{SystemTime, UNIX_EPOCH},
};
use tokio::task::JoinSet;

fn main() {
    println!("parallel write file!");
    let max_tasks = 200;
    let _ = fs::remove_file("/tmp/parallel");
    let file_ref = OpenOptions::new()
        .create(true)
        .write(true)
        .append(true)
        .open("/tmp/parallel")
        .unwrap();

    let mut set: JoinSet<()> = JoinSet::new();
    let rt = tokio::runtime::Runtime::new().unwrap();
    rt.block_on(async {
        loop {
            while set.len() >= max_tasks {
                set.join_next().await;
            }
            未做写互斥函数
            let mut file_ref = OpenOptions::new()
                .create(true)
                .write(true)
                .append(true)
                .open("/tmp/parallel")
                .unwrap();
            set.spawn(async move { write_line(&mut file_ref) });
        }
    });
}

fn write_line(file: &mut File) {
    for i in 0..1000 {
        let now = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
        let mut content = now.as_secs().to_string();
        content.push_str("_");
        content.push_str(&i.to_string());

        file.write_all(content.as_bytes()).unwrap();
        file.write_all("\n".as_bytes()).unwrap();
        file.write_all("\n".as_bytes()).unwrap();
    }
}


代码不复杂,tokio 实现一个并发runtime,写文件函数是直接写时间戳,为了方便展示乱序所以写入两次换行。

输出的文本大概长这样

1691287258_979





1691287258_7931691287258_301

1691287258_7431691287258_603

1691287258_8941691287258_47






1691287258_895
1691287258_553

1691287258_950
1691287258_980


1691287258_48
1691287258_302

1691287258_896
1691287258_744




1691287258_6041691287258_554


很明显,写入并未达到预期,间隔并不平均,函数内部的执行步骤是乱序的。

我们把上面的程序改造一下

use std::{
    fs::{self, File, OpenOptions},
    io::Write,
    sync::Arc,
    time::{SystemTime, UNIX_EPOCH},
};
use tokio::sync::Mutex;
use tokio::task::JoinSet;

fn main() {
    println!("parallel write file!");
    let max_tasks = 200;
    let _ = fs::remove_file("/tmp/parallel");
    let file_ref = OpenOptions::new()
        .create(true)
        .write(true)
        .append(true)
        .open("/tmp/parallel")
        .unwrap();

    let f = Arc::new(Mutex::new(file_ref));

    let mut set: JoinSet<()> = JoinSet::new();
    let rt = tokio::runtime::Runtime::new().unwrap();
    rt.block_on(async {
        loop {
            while set.len() >= max_tasks {
                set.join_next().await;
            }

            let mut file = Arc::clone(&f);
            set.spawn(async move { write_line_mutex(&mut file).await });
        }
    });
}

async fn write_line_mutex(mutex_file: &Arc<Mutex<File>>) {
    for i in 0..1000 {
        let mut f = mutex_file.lock().await;
        let now = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
        let mut content = now.as_secs().to_string();
        content.push_str("_");
        content.push_str(&i.to_string());

        f.write_all(content.as_bytes()).unwrap();
        f.write_all("\n".as_bytes()).unwrap();
        f.write_all("\n".as_bytes()).unwrap();
    }
}


这次我们用到了tokio::sync::Mutex,write_line_mutex函数在每次执行写任务以前先获取文件互斥锁。

看看这次的文件内容

1691288040_374

1691288040_374

1691288040_374

1691288040_375

1691288040_374

1691288040_374

1691288040_374

1691288040_374

1691288040_374

1691288040_374

1691288040_374

1691288040_374

1691288040_374

1691288040_374

1691288040_375

1691288040_375

1691288040_374

1691288040_375

1691288040_375

1691288040_375

1691288040_375

1691288040_375

1691288040_375

1691288040_375

1691288040_375

1691288040_375

1691288040_375


写入的格式正确,保证每次函数写函数完整执行。

关于文件写互斥这点事儿,今儿就聊到这。

完整源码

作者:京东科技 贾世闻

来源:京东云开发者社区

标签:--,文盘,write,let,file,1691288040,375,374,乱序
From: https://www.cnblogs.com/jingdongkeji/p/17622315.html

相关文章

  • typeScript学习-TS类型-字面量数据类型
    typeScript学习字面量数据类型://typeA=number|string//leta:A="abc"//typenum=number//letn:num=3typenum=1|2|3letn:num=2//错误情况//letn:num=4typeincreaseFlag=0|1functionisStartUp(increase:increaseFlag){if(in......
  • 微信对空格转换造成curl语句执行失败
    背景:同事给过来一条curl语句,很纯粹的一句语句,执行,报错了。提示:nosuchfileordirectory????一脸的黑人问号❓这么简单的语句,还能报错?并且还非常的莫名其妙。解决后来又发了一句过来,看起来一模一样的语句,执行,竟然又成功了。啊,神奇了。(服务并没有任何的改变)问题了下原来第......
  • 前端学习的顺序
    103人赞同了该回答先学重难点,也就是JS的三座大山:this、原型、异步。然后学习好标准库:Array(push、shift)Function(call、bind)Date、RegExp等接着就是浏览器提供的DOM和BOM(现在叫做WebAPIs)然后就是开始学习封装技巧:闭包、原型链、表驱动、最小知识原则、DRY原则、A......
  • LCM Sum[数论+树状数组]
    Problem-E2-Codeforces给一个区间[L,R],询问有多少三元组(i,j,k)满足L=<i<j<k<=r且lcm(i,j,k)>=i+j+k.正难则反。我们可以考虑它的补集。lcm<i+j+k,然后是i+j+k<3*k所以lcm<3k,又因为k是lcm的因数,所以lcm=k或者2k。那么答案变成了求L,R里lcm=k和2k的三元组的数目如果lcm=......
  • 申请阿里云免费SSL证书并配置https访问实战
                  文章转载:https://hashnode.blog.csdn.net/article/details/124555303......
  • $‘\r‘: command not found的解决方法
    缘起在Linux系统中,运行Shell脚本,出现了如下错误:one-more.sh:line1:$'\r':commandnotfound出现这样的错误,是因为Shell脚本在Windows系统编写时,每行结尾是\r\n,而在Linux系统中行每行结尾是\n,所以在Linux系统中运行脚本时,会认为\r是一个字符,导致运行错误。解决方法去除Shell......
  • vue 实现动态表单点击新增 增加一行输入框
    点击增加后会新增一行,点击每行后面的删除图标则会删除该行,新增按钮只会出现在最后一行<el-col:span="12"class="mb20"> <el-form-item :label="index==0?'选择原材料':''" v-for="(item,index)inform.productItemList"......
  • typeScript学习-TS类型-枚举
    typeScript学习枚举:enum枚举的定义:用来存放一组固定的常量的序列。枚举带来的好处:1、有默认值和可以自增值,节省编码时间2、语义更清晰,可读性增强,因为枚举是一种值类型的数据类型,方法参数可以明确参数类型为枚举类型enumWeekEnd{Monday="myMonday",//......
  • 【Eureka技术指南】「SpringCloud」从源码层面让你认识Eureka工作流程和运作机制(上)
    推荐超值课程:点击获取前言介绍了解到了SpringCloud,大家都应该知道注册中心,而对于我们从过去到现在,SpringCloud中用的最多的注册中心就是Eureka了,所以深入Eureka的原理和源码,接下来我们要进行讲解下eureka的源码分析,由此应运而产生的本章节的内容。基本原理EurekaServer提......
  • AES加密 flutter java后台用的 AES/CBC/PKCS5Padding
     可测试AES是否正确的网址https://www.toolhelper.cn/SymmetricEncryption/AES java后台代码如下publicstaticStringencrypt(StringclearText,Stringkey,Stringiv){byte[]result=null;try{byte[]key_bytes=toByte(MD5Util......