为什么这些多个异步 fs.createWriteStreams.write() 任务同时写入同一个文件不会重叠和混乱?

问题描述 投票:0回答:1

我编写了一个简单的node.js应用程序,我有一个问题。

这是程序的总体流程。

  1. 存在一个带有 URL 列表的本地文件,每个 URL 都占一个新行。例如,假设有 1,000 个 URL。

  2. 当读取每一新行 (URL) 时(实际上是在读取流中,使用“readLine”模块一次一行),回调将获取 URL 字符串并将其传递给另一个发出 http 请求的异步函数。

  3. 在这个http请求函数中,创建了一个https.request()。在请求的回调中,我只需获取响应并稍微转换一下 JSON 对象。事实上,我只是对 JSON 对象进行 stringify() 并将其值转换为 CSV 格式,这只是一个字符串。

  4. 我将此字符串传递给最终的 writeData() 函数,该函数旨在将此数据写入单个 .csv 文件。是的,同一个文件将用于接收所有这些异步 https 请求调用并存储一些本地数据。这里没什么奇怪的。 在 writeData() 函数中,我使用 fs.createWriteStream()。我向它传递了一个名为“output.csv”的文件并运行 ws.write(' ' + csvString)。

  • 现在这是我的问题/担忧...由于此 fs.createWriteStream().write() 函数的许多不同调用将被异步调用(记住,1000 个 URL),因此导致发生许多操作系统级写入,如何没有两个 writeStreams 完全相同的时间写入文件,因此互相混乱和截断?看起来每个 writeStream 都以一种漂亮、漂亮、有序的方式将其数据附加到文件中。 writeStream.write() 方法的回调是,下一个我期望的是,当一个写入流正在写入文件时,另一个写入流将同时创建并写入同一个文件,从而使文件内容变得混乱。

请记住,我不关心写入数据的顺序,但我确实关心在操作系统级别上文件上不会同时发生两次写入,从而使文件混乱。

**这里是writeData函数,供参考:

const fs = require("fs");
const writeData = (csvString) => {
  const ws = fs.createWriteStream(output.txt), { flags: "a" });
  ws.write("\n" + csvString, () => {
     console.log("A buffer has been written to the file: ");
     console.log(csvString);
  });
}

module.exports = writeData;

期望与现实:

这是实际输出的内容......看起来很好,有序,几乎同步出现。


"A buffer has been written to the file: "
<csvString prints here>
"A buffer has been written to the file: "
<next csvString prints here>
"A buffer has been written to the file: "
<next csvString prints here>
"A buffer has been written to the file: "
<next csvString prints here>

这是我期望的输出……这将是糟糕的、混乱的,并且根据操作系统决定给予异步进程/线程的时间,随机附加到文件的多个异步写入操作:

"A buffer has been written to the file: "
<csvString prints here>
 been written to the file: "
<next csvString prints here>
String prints here>
"A buffer has been wri
A buffer has been written to the file: "
<next csvStr
String prints here>
"A buffer has been written to the file: 
"r has been written to the file: "
<next csv

WriteFileSync()??

我思考后才意识到,也许 writeFileSync() 会清除我脑海中的所有担忧,因为这样我们就可以确定一次只有一个操作会写入/附加到文件。对于此应用程序来说,“阻塞”并不是一个大问题,因为每个对象到 output.csv 的写入大小并不大。

javascript node.js typescript v8
1个回答
0
投票

对于并发写入,您的担忧是有效的,但 Node.js 处理

fs.createWriteStream()
的方式在大多数情况下都是安全的。 Node.js 在内部缓冲写入流中的数据,确保即使多个异步操作尝试同时写入,它们也会被序列化。这可以防止您预期的数据混乱。

解释一下:

  1. 缓冲写入:每次对
    write()
    的调用一次处理一个。如果写入已经在进行中,则后续写入将在缓冲区中排队并按顺序执行。
  2. 内部队列:当尝试多个异步写入时,它们会被放入内部队列中并按顺序处理。

这就是为什么,即使有多个异步操作,您的写入也会显得有序且“同步”——写入实际上是由 Node.js 序列化的,而不是真正在文件上同时发生的。

您应该使用
writeFileSync()
吗?

使用

writeFileSync()
将确保严格的顺序顺序并避免任何潜在的竞争条件,因为它会阻塞线程直到写入完成。然而,这是以阻塞事件循环为代价的,这可能会降低整体性能,尤其是在有大量 URL 的情况下。

最佳实践:

  • 坚持使用
    fs.createWriteStream()
    ,但确保您的逻辑正确处理流结束或错误场景。
  • 如果您担心极端情况下的写入安全,您可以先将写入缓冲到内存中,然后将它们以受控块的形式刷新到文件中。

这是实现

writeData
功能的更好方法:

const fs = require("fs");
const writeData = (csvString) => {
  const ws = fs.createWriteStream("output.csv", { flags: "a" });
  ws.write("\n" + csvString, () => {
    console.log("A buffer has been written to the file: ");
    console.log(csvString);
  });
  ws.end();  // Closes the stream after writing
}

module.exports = writeData;

这会在每次写入后关闭写入流 (

ws.end()
),从而防止创建多个同时写入流。

© www.soinside.com 2019 - 2024. All rights reserved.