多个 fs.write 追加到同一个文件可以保证执行顺序吗?
Posted
技术标签:
【中文标题】多个 fs.write 追加到同一个文件可以保证执行顺序吗?【英文标题】:Can Multiple fs.write to append to the same file guarantee the order of execution? 【发布时间】:2017-03-10 14:57:57 【问题描述】:假设我们有这样一个程序:
// imagine the string1 to string1000 are very long strings, which will take a while to be written to file system
var arr = ["string1",...,"string1000"];
for (let i = 1; i < 1000; i++)
fs.write("./same/path/file.txt", arr[i], flag: "a");
我的问题是,will string1 to string1000 be gurantted to append to the same file in order?
由于 fs.write 是异步函数,我不确定每次调用 fs.write() 是如何真正执行的。我假设对每个字符串的函数调用应该放在another thread
中的某个位置(比如callstack
?),一旦前一个调用完成,就可以执行下一个调用。
我不确定我的理解是否准确。
编辑 1
正如在 cmets 和答案中一样,我看到 fs.write
对于多次写入同一个文件而不等待 callback
是不安全的。但是 writestream 呢?
如果我用下面的代码,能保证写的顺序吗?
// imagine the string1 to string1000 are very long strings, which will take a while to be written to file system
var arr = ["string1",...,"string1000"];
var fileStream = fs.createWriteFileStream("./same/path/file.txt", "flags": "a+" );
for (let i = 1; i < 1000; i++)
fileStream.write(arr[i]);
fileStream.on("error", () => // do something);
fileStream.on("finish", () => // do something);
fileStream.end();
任何 cmets 或更正都会有所帮助!谢谢!
【问题讨论】:
如果你的意思是fs.writeFile()
,那么文档指出“......在同一个文件上多次使用fs.writeFile
而不等待回调是不安全的”.
请注意,由于 node.js 中缓冲的工作方式,不等待回调不会影响顺序或写入。不安全的是缓冲区溢出的可能性。所以风险在于丢失写入,而不是无序写入。
@slebetman,感谢您的回复。但是这里的缓冲到底是什么意思呢?
@Lubor:核心 node.js 通过为每个打开的文件生成一个线程来管理文件/磁盘 I/O。当您写入时,您实际上并没有写入文件。相反,您要做的是向此 I/O 线程发送消息。所以这个 I/O 线程需要将此消息存储在 RAM 中的某个位置。这是 I/O 缓冲区。我相信它的大小在编译时是固定的。然后,只要文件可写,线程就会执行适当的异步 I/O 循环,将数据从该缓冲区写入磁盘(当文件的操作系统写入缓冲区为空时,您的操作系统再次不会写入磁盘,而是以类似的方式缓冲)
@Lubor:从我在回答类似问题时读到的代码中,我可以看到进入此 I/O 缓冲区并从该缓冲区中取出以写入磁盘的字符串的顺序得到保证。缓冲区无法重新排列,因此您写入的顺序就是将写入磁盘的顺序。但如果缓冲区已满,您的写入将被忽略。
【参考方案1】:
docs 这么说
请注意,在同一个文件上多次使用
fs.write
而不等待回调是不安全的。对于这种情况,强烈建议使用 fs.createWriteStream。
使用流是有效的,因为流本质上保证写入它们的字符串的顺序与从中读出的顺序相同。
var stream = fs.createWriteStream("./same/path/file.txt");
stream.on('error', console.error);
arr.forEach((str) =>
stream.write(str + '\n');
);
stream.end();
另一种仍然使用fs.write
但也确保事情发生的方法是使用promise来维护顺序逻辑。
function writeToFilePromise(str)
return new Promise((resolve, reject) =>
fs.write("./same/path/file.txt", str, flag: "a", (err) =>
if (err) return reject(err);
resolve();
);
);
// for every string,
// write it to the file,
// then write the next one once that one is finished and so on
arr.reduce((chain, str) =>
return chain
.then(() => writeToFilePromise(str));
, Promise.resolve());
【讨论】:
那么 createWriteFileStream 呢?如果我使用filestream,我们可以保证顺序吗? 谢谢!但是你是说 stream.write(str) 是同步操作吗?我一直认为这是异步的。 @Lubor 仅供参考:根据定义,流保证写入它的顺序与“输出”的顺序相同(在这种情况下,写入文件)。 @tcooc 哇,这个定义很有意义!谢谢! @Lubor 我的错误,我的措辞是错误的。stream.write
是异步的。【参考方案2】:
您可以使用节点的读/写锁定来同步对文件的访问,请参阅以下示例,您可以阅读documentation
var ReadWriteLock = require('rwlock');
var lock = new ReadWriteLock();
lock.writeLock(function (release)
fs.appendFile(fileName, addToFile, function(err, data)
if(err)
console.log("write error"); //logging error message
else
console.log("write ok");
release(); // unlock
);
);
【讨论】:
【参考方案3】:我遇到了同样的问题,并为我的项目编写了一个 NPM 包来解决它。它的工作原理是将数据缓冲在一个数组中,然后等待事件循环结束,在对fs.appendFile
的一次调用中连接和写入数据:
const SeqAppend = require('seqappend');
const writeLog = SeqAppend('log1.txt');
writeLog('Several...');
writeLog('...logged...');
writeLog('.......events');
【讨论】:
以上是关于多个 fs.write 追加到同一个文件可以保证执行顺序吗?的主要内容,如果未能解决你的问题,请参考以下文章