【问题标题】:Dart File.writeAsString() method does not write to file if await is not done immediately如果没有立即完成等待,Dart File.writeAsString() 方法不会写入文件
【发布时间】:2019-03-02 12:06:28
【问题描述】:

我的以下 Dart 代码的行为与我预期的不同:

final File file = File("result.csv");

Future send(String message) async {
  try {
    await file.writeAsString(message + '\n',
        mode: FileMode.append, flush: true);
  } catch (e) {
    print("Error: $e");
  }
  return await file.length();
}

main() async {
  final futures = <Future>[];
  for (int i = 0; i < 100; i++) {
    futures.add(send("$i"));
  }
  for (int i = 0; i < 100; i++) {
    print(await futures[i]);
  }
}

我希望在第二个循环中对await futures[i] 的每次调用返回后立即写入文件。然而,这似乎并没有发生。

对于从 0 到 99 的每个索引,该文件应包含一行。但它包含一行 99,后跟一个空行。第二个循环中的打印调用总是打印相同的文件长度,3

事件循环似乎以某种方式合并了调用,并且只实际执行了最后一个调用,尽管我仍然在第二个循环中等待了 100 个不同的未来。

为什么会发生这种情况,我怎样才能让期货运行而不立即等待它们(我真的只需要稍后等待,当所有对 send 的调用都已完成时)?

【问题讨论】:

    标签: dart


    【解决方案1】:

    使用循环:

    for (int i = 0; i < 100; i++) {
      futures.add(send("$i"));
    }
    

    立即调用多个send,每个send同时打开并在文件中写入一个字符串:你有一个竞争条件,最后碰巧只有一条消息写入文件。

    使用返回未来的函数列表

    通过闭包,可以为文件访问实现一个序列化版本,从而避免竞争条件。

    创建一个返回future的函数列表,而不是创建future列表: 如果您在循环中调用和等待此类函数,则会按顺序访问共享文件资源:

    import 'dart:io';
    
    final File file = File("result.csv");
    
    typedef Future SendFunction();
    
    Future send(String message) async {
      try {
        await file.writeAsString(message + '\n',
            mode: FileMode.append, flush: true);
      } catch (e) {
        print("Error: $e");
      }
      var l = await file.length();
      return l;
    }
    
    main() async {
    
      final futures = List<SendFunction>();
      //final futures = <Future>[];
    
      for (int i = 0; i < 100; i++) {
        //futures.add(send("$i"));
        futures.add(() => send("$i"));
      }
    
      for (int i = 0; i < 100; i++) {
        print(await futures[i]());
        //print(await futures[i]);
      }
    }
    

    同步包

    synchronized 提供锁定机制来防止并发访问异步代码。

    在这种情况下,它可以用来避免同时访问result.csv文件:

    import 'dart:io';
    import 'package:synchronized/synchronized.dart';
    
    final File file = File("result.csv");
    final lock = new Lock();
    
    Future send(String message) async {
      try {
        await file.writeAsString(message + '\n',
            mode: FileMode.append, flush: true);
      } catch (e) {
        print("Error: $e");
      }
      return await file.length();
    }
    
    main() async {
      final futures = <Future>[];
      for (int i = 0; i < 100; i++) {
        futures.add(lock.synchronized(() => send("$i")));
      }
      for (int i = 0; i < 100; i++) {
        print(await futures[i]);
      }
    }
    

    【讨论】:

    • 谢谢。我不知道在 Dart 中可能会出现这样的同步问题,因为用户代码无法创建线程(在没有隔离的情况下)。我想这只是一个问题,因为File.writeAsString() 本身不是用 Dart 写的?
    • 我不能真正谈论飞镖内部,但我认为没有。此行为与并发调用 send 而不等待完成 (futures.add(send("$i"))) 直接执行所有 send 尽快并发,没有文件访问同步的事实有关。
    • 应该不需要同步,因为据我所知文件系统本身会处理这个问题。我所期望的只是乱序写入,这在我的情况下是可以的。
    • 我创建了一个错误,看起来 Dart 团队认为可以在内部使用原子版本的 append,这将解决这个问题:github.com/dart-lang/sdk/issues/36087
    猜你喜欢
    • 2018-12-20
    • 2023-03-11
    • 1970-01-01
    • 2023-03-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-03-22
    • 1970-01-01
    相关资源
    最近更新 更多