0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

如何利用Mutex解决并发写文件乱序的问题?

jf_wN0SrCdH 来源:京东云开发者 2023-08-12 09:54 次阅读

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

use std::{
    fs::{self, File, OpenOptions},
    io::{Write},
    sync::Arc,
    time::{SystemTime, UNIX_EPOCH},
};
use tokio::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::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("
".as_bytes()).unwrap();
        file.write_all("
".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::Mutex;
use tokio::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::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>) {
    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("
".as_bytes()).unwrap();
        f.write_all("
".as_bytes()).unwrap();
    }
}

这次我们用到了tokio::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

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

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






审核编辑:刘清

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • Unix系统
    +关注

    关注

    0

    文章

    15

    浏览量

    9634
  • ARC
    ARC
    +关注

    关注

    0

    文章

    42

    浏览量

    16458
  • rust语言
    +关注

    关注

    0

    文章

    57

    浏览量

    3006

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

文章出处:【微信号:Rust语言中文社区,微信公众号:Rust语言中文社区】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    详解linux内核中的mutex同步机制

    在linux内核中,互斥量(mutex,即mutual exclusion)是一种保证串行化的睡眠锁机制。和spinlock的语义类似,都是允许一个执行线索进入临界区,不同的是当无法获得锁的时候
    的头像 发表于 05-13 08:56 6765次阅读
    详解linux内核中的<b class='flag-5'>mutex</b>同步机制

    Linux内核同步机制mutex详解

    在linux内核中,互斥量mutex是一种保证CPU串行运行的睡眠锁机制。和spinlock类似,都是同一个时刻只有一个线程进入临界资源,不同的是,当无法获取锁的时候,spinlock原地自旋,而mutex则是选择挂起当前线程,进入阻塞状态。所以,
    发表于 06-26 16:05 1041次阅读

    Raw-os mutex

    1mutex 支持FIFO 和PRIO 的任务阻塞策略,如果是FIFO 的话阻塞队列的顺序是按照先来后到的次序去排列阻塞任务,PRIO 策略的话是按照优先级的排序。具体的设置可以直接设置这个结构体中
    发表于 02-27 14:03

    关于mutex

    mutex的出现是为了解决优先级反转的问题,由于优先级反转对实时性影响太大,所以mutex 的稳定性直接影响了实时性。纵观目前多种实时操作系统mutex 的设计原理是多多少少有一点问题的,raw
    发表于 02-27 14:34

    labview写入access数据库乱序问题

    用database insert 插入数据,移位寄存器当作顺序编号,发现不管循环框频率设多少,到编号713这里必定会跳到737,然后中间这段会在1035后出现,其他地方也有类似的乱序,多次写入乱序
    发表于 08-09 10:23

    互斥量Mutex相关资料推荐

    这里目录标题概述API二级目录三级目录概述APItx_mutex_createtx_mutex_deletetx_mutex_gettx_mutex_put二级目录三级目录
    发表于 02-22 07:40

    适用于IPTV大并发应用的文件格式

    分析交互式网络电视(IPTV)大并发应用的特性,提出一种适用于IPTV大并发应用的服务器内部文件格式cl4文件格式。该文件格式采用了符合IP
    发表于 04-15 10:02 17次下载

    什么是CPU分枝/乱序执行?

    什么是分枝/乱序执行?   分枝(branch)是指程序运行时需要改变的节点。分枝有无条件分枝和有条件分枝,其中无条件分枝只
    发表于 02-04 10:49 870次阅读

    如何采用DATA进行Flash的在线烧

    自加载后DSP能够正常运行,关键是Flash中原程序代码的正确烧。CCS编译生成的.out格式文件不能直接用于Flash烧,在TI公司给出的技术文档闭中,首先将.out文件
    的头像 发表于 02-06 08:51 3587次阅读
    如何采用DATA进行Flash的在线烧<b class='flag-5'>写</b>

    Linux多线程同步互斥量Mutex详解

    pthread_mutex_destroy(pthread_mutex_t *mutex);头文件:返回值: 成功则返回0, 出错则返回错误编号.说明: 如果使用默认的属性初始化互斥
    发表于 04-02 14:45 293次阅读

    ThreadX(七)------互斥量Mutex

    这里目录标题概述API二级目录三级目录概述APItx_mutex_createtx_mutex_deletetx_mutex_gettx_mutex_put二级目录三级目录
    发表于 12-28 19:29 8次下载
    ThreadX(七)------互斥量<b class='flag-5'>Mutex</b>

    如何用Actix去一个类似于Facemash的小项目呢

    同时,和 MongoDB 不同的是,这里需要使用 Mutex 进行封装,因为极有可能多个出现多个线程并发获取Id;
    的头像 发表于 10-17 09:47 874次阅读
    如何用Actix去<b class='flag-5'>写</b>一个类似于Facemash的小项目呢

    编译器的乱序策略

    这篇文章的目的,是想明确下cpu指令乱序这件事。只要是熟悉计算机底层系统的同学就会知道,程序里面的每行代码的执行顺序,有可能会被编译器和cpu根据某种策略,给打乱掉,目的是为了性能的提升,让指令的执行能够尽可能的并行起来。
    的头像 发表于 05-19 14:46 679次阅读
    编译器的<b class='flag-5'>乱序</b>策略

    乱序文件如何重新命名编号

    如下图所示,在本地文件夹中有这样一堆视频文件,在这种情况下并不是乱序的。 但是将其上传到网盘中后,就会经常变成乱序。即它们会按照1、10、11、2、20这样排序,并不方便我们按顺序去依
    的头像 发表于 10-21 10:50 524次阅读
    <b class='flag-5'>乱序文件</b>如何重新命名编号

    进程文件会丢失数据吗

    进程文件(使用缓冲 IO)过程中,一半的时候,进程发生了崩溃,会丢失数据吗? 答案,是不会的。 因为进程在执行 write (使用缓冲 IO)系统调用的时候,实际上是将文件数据写到
    的头像 发表于 11-13 10:57 631次阅读
    进程<b class='flag-5'>写</b><b class='flag-5'>文件</b>会丢失数据吗