在实际开发过程中,我们可能会遇到并发写文件的场景,如果处理不当很可能出现文件内容乱序问题。下面我们通过一个示例程序描述这一过程并给出解决该问题的方法。
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
+关注
关注
0文章
42浏览量
16458 -
rust语言
+关注
关注
0文章
57浏览量
3006
原文标题:文盘Rust -- Mutex解决并发写文件乱序问题
文章出处:【微信号:Rust语言中文社区,微信公众号:Rust语言中文社区】欢迎添加关注!文章转载请注明出处。
发布评论请先 登录
相关推荐
详解linux内核中的mutex同步机制
在linux内核中,互斥量(mutex,即mutual exclusion)是一种保证串行化的睡眠锁机制。和spinlock的语义类似,都是允许一个执行线索进入临界区,不同的是当无法获得锁的时候
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次下载
如何采用DATA进行Flash的在线烧写
自加载后DSP能够正常运行,关键是Flash中原程序代码的正确烧写。CCS编译生成的.out格式文件不能直接用于Flash烧写,在TI公司给出的技术文档闭中,首先将.out文件
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次下载
编译器的乱序策略
写这篇文章的目的,是想明确下cpu指令乱序这件事。只要是熟悉计算机底层系统的同学就会知道,程序里面的每行代码的执行顺序,有可能会被编译器和cpu根据某种策略,给打乱掉,目的是为了性能的提升,让指令的执行能够尽可能的并行起来。
乱序文件如何重新命名编号
如下图所示,在本地文件夹中有这样一堆视频文件,在这种情况下并不是乱序的。 但是将其上传到网盘中后,就会经常变成乱序。即它们会按照1、10、11、2、20这样排序,并不方便我们按顺序去依
进程写文件会丢失数据吗
进程写文件(使用缓冲 IO)过程中,写一半的时候,进程发生了崩溃,会丢失数据吗? 答案,是不会的。 因为进程在执行 write (使用缓冲 IO)系统调用的时候,实际上是将文件数据写到
评论