关于Nodejs fs.writeFile 异步方法几个同时执行的顺序问题

关于Nodejs fs.writeFile 异步方法几个同时执行的顺序问题

求教node,写同一个文件会不会产生锁使结果总为最后的数值? 假设我代码是

fs.writeFile "file/sign", 1 , (err)->
  throw err if (err)
fs.writeFile "file/sign", 3 , (err)->
  throw err if (err)
fs.writeFile "file/sign", 5 , (err)->
  throw err if (err)
fs.writeFile "file/sign", 7 , (err)->
  throw err if (err) 

我不同地方触发写不同的数值,最后写到文件里的是不是一定是7,最后一次执行异步写文件?,全部在写同一个文件系统触发顺序就想这样


5 回复

关于Node.js fs.writeFile 异步方法几个同时执行的顺序问题

在Node.js中,fs.writeFile 是一个异步函数,这意味着它不会阻塞程序的执行。当你同时调用多个 fs.writeFile 操作时,这些操作将并行执行,但最终的结果可能会因为操作系统级别的文件锁定机制而有所不同。

示例代码

const fs = require('fs');

// 第一个写入操作
fs.writeFile("file/sign", 1, (err) => {
  if (err) throw err;
});

// 第二个写入操作
fs.writeFile("file/sign", 3, (err) => {
  if (err) throw err;
});

// 第三个写入操作
fs.writeFile("file/sign", 5, (err) => {
  if (err) throw err;
});

// 第四个写入操作
fs.writeFile("file/sign", 7, (err) => {
  if (err) throw err;
});

结果分析

在上述代码中,所有的 fs.writeFile 操作几乎会同时启动。然而,由于文件系统的异步特性,每个写入操作的实际完成时间可能有所不同。操作系统会处理这些并发写入请求,并决定它们的执行顺序。

通常情况下,最后一个写入操作(在这个例子中是 7)可能会覆盖之前的写入结果,因为文件写入操作通常是原子性的,即一旦某个写入操作成功完成,它会覆盖文件中的现有内容。

实验验证

为了验证这一点,你可以添加一些延迟,以观察是否会影响结果:

const fs = require('fs');

setTimeout(() => {
  fs.writeFile("file/sign", 1, (err) => {
    if (err) throw err;
  });
}, 100);

setTimeout(() => {
  fs.writeFile("file/sign", 3, (err) => {
    if (err) throw err;
  });
}, 200);

setTimeout(() => {
  fs.writeFile("file/sign", 5, (err) => {
    if (err) throw err;
  });
}, 300);

setTimeout(() => {
  fs.writeFile("file/sign", 7, (err) => {
    if (err) throw err;
  });
}, 400);

在这种情况下,即使有延迟,最后一个写入操作仍然可能覆盖之前的写入结果。

总结

  • 结论:如果你同时调用多个 fs.writeFile 操作写入同一个文件,最终结果可能是最后一次写入的内容。这是因为文件系统通常会覆盖现有的文件内容。

  • 解决方案:如果你需要确保某些特定的写入顺序或避免覆盖问题,可以考虑使用其他同步或顺序化的机制,例如使用 fs.appendFile 或者在写入之前检查文件内容。

希望这能帮助你理解Node.js中 fs.writeFile 的行为和如何处理并发写入问题。


自己尝试了好几下,发现异步写入的时候会发生两种情况, 连续两次写入后面那次写入有小概率丢失,(应该是先执行了写入后面的再执行写入前面的了), 如

fs.writeFile "file/sign", 1 , (err)->
  throw err if (err)
fs.writeFile "file/sign", 3 , (err)->
  throw err if (err)

查看文件结果可能是1 还有就是 当先写入一个很长的字符串,再写入一个比较短的字符串,长字符串不足部分会被保留 比如

fs.writeFile "file/sign", 1234567 , (err)->
  throw err if (err)
fs.writeFile "file/sign", 321 , (err)->
  throw err if (err)

最终会有很大的概率变成 3214567

不知道有没大神解释一下啊原因

首先,大家要求:代码格式化做好,表述清楚一些;然后,个人观点:你的写入明显依赖操作顺序,异步无法确定写入的最后内容~3214567的问题,应该和写入模式有关系,建议查查API

文件写入顺序,和你的逻辑有关系。如果你的逻辑是异步执行的,那么写入顺序就不好预测了。写文件有一个同步的fs.writeFileSync,用这个,只要保证你程序是同步写入的,那么写入顺序就可以控制了。也就是你要求的7

在Node.js中,fs.writeFile 是一个异步操作,它并不会因为多个请求写入同一个文件而自动加锁。每个 fs.writeFile 操作都会独立地完成,但最终写入文件的内容取决于操作系统文件系统的缓存和实际磁盘写入的时机。

在你的例子中,可能会发生的情况是:

  • 每个 fs.writeFile 都会尝试将文件内容设置为最新的值。
  • 如果这些操作非常接近(几乎同时),那么最终写入文件的内容可能是最后一个调用的值,即7。
  • 但是,如果某些操作花费了更长时间,或者由于某些原因(例如网络延迟或磁盘I/O繁忙)导致某些操作延后,那么最终文件的内容可能并不是7。

为了确保文件写入的顺序性和一致性,可以使用 fs.appendFile 或者通过使用互斥锁来控制写入的顺序。这里提供一个使用互斥锁的例子,你可以使用 mkdirplockfile 来实现:

const fs = require('fs');
const mkdirp = require('mkdirp');
const lockfile = require('lockfile');

mkdirp('file', (err) => {
  if (err) throw err;

  // 加锁
  lockfile.lock('file/sign.lock', (err) => {
    if (err) throw err;

    fs.writeFile("file/sign", 1, (err) => {
      if (err) throw err;
      
      fs.writeFile("file/sign", 3, (err) => {
        if (err) throw err;
        
        fs.writeFile("file/sign", 5, (err) => {
          if (err) throw err;
          
          fs.writeFile("file/sign", 7, (err) => {
            if (err) throw err;

            // 解锁
            lockfile.unlock('file/sign.lock', (err) => {
              if (err) throw err;
            });
          });
        });
      });
    });
  });
});

这段代码中,我们首先创建目录(如果还没有创建),然后使用互斥锁确保在同一时间只有一个写入操作进行。这样可以保证文件写入的顺序。

回到顶部