I have the following Dart code that doesn't behave as I expected:
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]);
}
}
I expected the file to be written as soon as each call to await futures[i]
in the second loop returned. However this does not seem to be happening.
The file should contain one line for each index from 0 to 99. But it contains a line with 99
followed by an empty line. And the print calls in the second loop always print the same file length, 3
.
The event loop seems to be somehow merging the calls and only actually executing the last call, even though I still get 100 different futures that I await in the second loop.
Why is this happening and how can I allow the futures to run without awaiting them immediately (I really need to await only later, when all of the calls to send
have been made)?
With the loop:
for (int i = 0; i < 100; i++) {
futures.add(send("$i"));
}
multiple send
are immediately invoked and each one concurrently open and write a string in the file: you have a race condition and at the ends it happens that only one message it is written to the file.
With a closure it is possible to implement a sequenzialized version for file access that avoid the race condition.
Instead of creating a list of futures, create a list of functions that returns a future: The shared file resource is accessed sequentially if you call and await such functions in a loop:
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 offers lock mechanism to prevent concurrent access to asynchronous code.
In this case it may be used to avoid concurrent access to result.csv
file:
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]);
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With