node写文件的并发问题,是同时写入,还是一个完成后再执行下一个?
发布于 5 年前 作者 jyk0011 7481 次浏览 来自 问答

先上测试代码吧。

var fs = require(‘fs’); var i = 0; fs.writeFile(’./json’+i+’.json’, 1,function(err){ if(err) console.log(‘写文件操作失败’); else console.log(‘写文件操作成功 -1’); }); //i++; fs.writeFile(’./json’+i+’.json’, 111,function(err){ if(err) console.log(‘写文件操作失败’); else console.log(‘写文件操作成功-2’); }); //i++;

fs.writeFile(’./json’+i+’.json’, 11111,function(err){ if(err) console.log(‘写文件操作失败’); else console.log(‘写文件操作成功-3’); }); //i++;

fs.writeFile(’./json’+i+’.json’, 1111111,function(err){ if(err) console.log(‘写文件操作失败’); else console.log(‘写文件操作成功-4’); }); //i++;

fs.writeFile(’./json’+i+’.json’, 111111111,function(err){ if(err) console.log(‘写文件操作失败’); else console.log(‘写文件操作成功-5’); }); //i++;

fs.writeFile(’./json’+i+’.json’, 222222222222,function(err){ if(err) console.log(‘写文件操作失败-6’); else console.log(‘写文件操作成功-6’); });

同时发起六个写文件的操作,文件名称相同,也就是往同一个文件里面写数据。 如果是单线程的话,那么是一个一个顺序执行, 先执行第一个,把“1”写进去, 然后执行第二个,把“111”写进去,同时覆盖上一次的操作“1” 然后执行第三个,以此类推,最后文件里面剩下的只有“222222222222”。

但是多次运行之后,结果很诡异。有时候文件里是“222222222222”,有的时候是“1”,这个可以理解为“入栈”的先后顺序不一致。

但是有一次,文件内容是“111111122222”,还有一次内容是“111111111222”。

这是什么鬼?这是哪来的?如果是单线程,一次只执行一个写入操作,内容绝对不会乱,会覆盖在意料之中。

难道是传说中的“线程不安全”,但是node不是单线程的吗?

难道六个写入操作,是同时进行的吗?而且还互相影响?!

7 回复

楼主是不是之前刚写一个关于node单线程和多线程的问题?你用的都是异步写文件api,你需要学习下node的event loop原理,如果你使用同步写文件api,结果就是你想的那样!

@zhangmingfeng 如果只是返回顺序不一定,那么没啥大事,但是文件内容乱套了,这就麻烦了呀。如果起了一个http服务,想写个日志,记到文本里面,内容会不会也乱套了呀。正在写测试代码,看看运行情况。

因为你是同时去改这个文件的内容。

文件 IO 由 C 部分的线程池负责调度,并没有谁先谁后,并没有等前面一个写完成了,再写后面一个,所以就有可能一会是这个结果,一会又是另一个。

如果想要 “不写乱”,那么就需要用同步 API,或者异步的 async await保证写入顺序。

Javascript 的是单线程的,单线程是没有什么线程安全问题的,所以也不会有什么线程锁。

如果开启集群模式,就是多线程的,这种情况下才有线程安全问题。

比较典型的例子就是写日志,几个线程同时对一个日志文件写入,那么就有可能乱套了。

所以一般的解决办法:

  1. 开启一个独立的线程 Agent,专门写日志,N 个读 1 个写,就不会乱
  2. 各个线程写入自己的日志文件,比如 log-1log-2log-3
  3. 加文件锁,每次写入日志都检查是否有文件锁,然后给文件加锁,完成后释放锁

@axetroy 谢谢,我是不是可以理解为,node使用c来实现IO操作,也就是文件的读写,所以是多线程的模式,也就有了线程是否安全的问题。

采用异步(回调)的方式,就变成了多线程,所以不安全。 可以采用同步(await)的方式,这样就是一个一个读写了,对吧。

不过有一个解答说,可以用追加文件内容的方式来实现,我试了一下,不会丢失记录了。 还是异步的形式。

异步I/O是没有顺序的,底层也是线程池分配多个线程来处理IO。所谓单线程是指的运行同步代码和挂起异步操作的主线程,如果有异步操作交给多线程,没有就按照从上到下的顺序执行

@jyk0011 可能是在node回调以及传统线程语言之间纠缠不清,在node里写的js代码,永远都是单线程运行,启动多个进程也是单线程,不共享内存的进程(通过进程间通讯,你可以把它拿来做线程理解),不管是回调还是用await,都是单线程异步的。按顺序写writefile只是按照顺序告知node的内部线程池去开始执行这个命令,而至于哪个命令先运行成功,那都是不确定的,你就可以理解为不安全的,await相当于在你第一个回调里调用第二个,然后在第二个回调里面调用第三个,这样就确保了你第一个命令完成后再运行第二个命令,强制加了个顺序

回到顶部