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

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

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

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

完整源码

作者:京东科技 贾世闻

来源:京东云开发者社区

文盘Rust -- Mutex解决并发写文件乱序问题的更多相关文章

  1. Python中,os.listdir遍历纯数字文件乱序如何解决

    Python中,os.listdir遍历纯数字文件乱序如何解决 日常跑深度学习视觉相关代码时,常常需要对数据集进行处理.许多图像文件名是利用纯数字递增的方式命名.通常所用的排序函数sort(),是按照 ...

  2. Python3并发写文件

    使用python2在进行并发写的时候,发现文件会乱掉,就是某一行中间会插入其他行的内容. 但是在使用python3进行并发写的时候,无论是多进程,还是多线程,都没有出现这个问题,难道是python3的 ...

  3. 文盘Rust -- 把程序作为守护进程启动

    当我们写完一个服务端程序,需要上线部署的时候,或多或少都会和操作系统的守护进程打交道,毕竟谁也不希望shell关闭既停服.今天我们就来聊聊这个事儿. 最早大家部署应用的通常操作是 "nohu ...

  4. 文盘Rust -- 本地库引发的依赖冲突

    作者:京东科技 贾世闻 问题描述 clickhouse 的原生 rust 客户端目前比较好的有两个clickhouse-rs 和 clickhouse.rs .clickhouse-rs 是 tcp ...

  5. 文盘Rust -- struct 中的生命周期

    最近在用rust 写一个redis的数据校验工具.redis-rs中具备 redis::ConnectionLike trait,借助它可以较好的来抽象校验过程.在开发中,不免要定义struct 中的 ...

  6. 文盘Rust -- 给程序加个日志

    作者:贾世闻 日志是应用程序的重要组成部分.无论是服务端程序还是客户端程序都需要日志做为错误输出或者业务记录.在这篇文章中,我们结合[log4rs](https://github.com/estk/l ...

  7. 文盘Rust -- rust 连接云上数仓 starwift

    作者:京东云 贾世闻 最近想看看 rust 如何集成 clickhouse,又犯了好吃懒做的心理(不想自己建环境),刚好京东云发布了兼容ck 的云原生数仓 Starwfit,于是搞了个实例折腾一番. ...

  8. 文盘Rust -- 用Tokio实现简易任务池

    作者:京东科技 贾世闻 Tokio 无疑是 Rust 世界中最优秀的异步Runtime实现.非阻塞的特性带来了优异的性能,但是在实际的开发中我们往往需要在某些情况下阻塞任务来实现某些功能. 我们看看下 ...

  9. 解决靶机Bee-Box 键盘乱序问题

    Bee-Box介绍 Bee-box官方称呼BWAPP,buggy web Application 这是一个集成了各种常见漏洞和最新漏洞的开源Web应用程序,目的是帮助网络安全爱好者.开发人员和学生发现 ...

  10. 用读写锁三句代码解决多线程并发写入文件 z

    C#使用读写锁三句代码简单解决多线程并发写入文件时提示“文件正在由另一进程使用,因此该进程无法访问此文件”的问题 在开发程序的过程中,难免少不了写入错误日志这个关键功能.实现这个功能,可以选择使用第三 ...

随机推荐

  1. 音视频八股文(5)--SDL音视频渲染实战。会使用就行,不需要深究。

    01-SDL子系统 SDL将功能分成下列数个子系统(subsystem): SDL_INIT_TIMER:定时器 SDL_INIT_AUDIO:音频 SDL_INIT_VIDEO:视频 SDL_INI ...

  2. 2023-04-24:用go语言重写ffmpeg的muxing.c示例。

    2023-04-24:用go语言重写ffmpeg的muxing.c示例. 答案2022-04-24: 本程序的大体过程如下: 打开输出文件并写入头部信息. 添加音频和视频流,并为每个流创建 AVCod ...

  3. 2022-12-23:portainer是docker的web可视化工具。如果根据docker部署去写yaml,默认local是k8s,而不是docker,这不符合需求,需要修改yaml。请问部署在

    2022-12-23:portainer是docker的web可视化工具.如果根据docker部署去写yaml,默认local是k8s,而不是docker,这不符合需求,需要修改yaml.请问部署在 ...

  4. 2020-11-21:java中,什么是跨代引用?

    福哥答案2020-11-21: [答案来自此链接:](http://bbs.xiangxueketang.cn/question/404) 跨代引用常出现与 CMS 分代收集时需要使用可达性分析判断对 ...

  5. 2022-07-23:给定N件物品,每个物品有重量(w[i])、有价值(v[i]), 只能最多选两件商品,重量不超过bag,返回价值最大能是多少? N <= 10^5, w[i] <= 10^5, v

    2022-07-23:给定N件物品,每个物品有重量(w[i]).有价值(v[i]), 只能最多选两件商品,重量不超过bag,返回价值最大能是多少? N <= 10^5, w[i] <= 1 ...

  6. 2022-03-08:给定一棵树的头节点head, 请按照题意,保留节点,没有保留的节点删掉。 树调整完之后,返回头节点。

    2022-03-08:给定一棵树的头节点head, 请按照题意,保留节点,没有保留的节点删掉. 树调整完之后,返回头节点. 答案2022-03-08: 递归.当前节点描黑或者子节点描黑,那就保留:否则 ...

  7. 2021-08-26:长度为N的数组arr,一定可以组成N^2个数字对。例如arr = [3,1,2],数字对有(3,3) (3,1) (3,2) (1,3) (1,1) (1,2) (2,3) (2

    2021-08-26:长度为N的数组arr,一定可以组成N^2个数字对.例如arr = [3,1,2],数字对有(3,3) (3,1) (3,2) (1,3) (1,1) (1,2) (2,3) (2 ...

  8. Ubuntu 18.04 (Bionic) 简单快速的安装mongodb

    按步骤走,不带脑子式安装(注意4.0版本mongodb官方已经不再支持,以下代码中可以修改mongodb版本号安装,目前最新版为6.0,如果懒得改直接用也可以,文章后边第三章第一条代码会直接升级为最新 ...

  9. Java如何实现去重?这是在炫技吗?

    大家好,我3y啊.由于去重逻辑重构了几次,好多股东直呼看不懂,于是我今天再安排一波对代码的解析吧.austin支持两种去重的类型:N分钟相同内容达到N次去重和一天内N次相同渠道频次去重. Java开源 ...

  10. PowerBuilder从入坑到放弃(二)编码规范

    前言 上一篇我们从0到1用pb开发了一个helloworld程序,并成功将开发的程序编译打包并且制作了安装包. 程序员最讨厌的莫过于写文档和别人不写注释. 不知道大家会不会和我一样,在找bug时,有段 ...