Dart 多线程 - 被等待阻塞行为所混淆



据我了解,您可以在 Dart 中等待一个阻止当前执行的异步函数的未来,直到将来返回结果。

例如,调用inSequence()需要三秒钟来打印其输出。

// Return a String after a one second delay. No surprises here.
Future<String> long() async {
await Future.delayed(Duration(seconds: 1));
return ":)";
}

void inSequence() async {
// sequentially wait on the futures - takes 3 seconds
print(await long());
print(await long());
print(await long());
}

或者,如果您想一次运行所有期货,您可以调用Future.wait([...])。然后所有期货将执行,一旦准备好任何后续的异步调用,结果将可用,因此下面的inParallel()只需要一秒钟即可执行。

void inParallel() async {
var f1 = long();
var f2 = long();
var f3 = long();

Future.wait([f1,f2,f3]); // wait on all futures in parallel.

print(await f1);
print(await f2);
print(await f3);
}

以上所有内容对我来说都很有意义。但是,在下面查看以confusing()代码的行为并不像我期望的那样。我希望这个函数需要三秒钟来执行,因为在打印字符串之前等待每个await,然后继续下一个,直到函数在三秒后完成。 实际上,此功能只需1秒钟,即可一次打印所有结果。 为什么?这是如何工作的?

void confusing() async {
var f1 = long();
var f2 = long();
var f3 = long();

print(await f1);
print(await f2);
print(await f3);
}

谢谢。

或者,如果你想一次运行所有期货,你可以调用Future.wait([...])。然后所有期货将执行,一旦准备好任何后续异步调用,结果将可用,因此下面的inParallel()只需要一秒钟即可执行。

不一定。 使用Future.wait本身不会导致操作并行执行;如果可以的话,它只是不会阻止它们并行发生。1

实际上,此功能只需1秒钟,即可一次打印所有结果。 为什么?这是如何工作的?

void confusing() async {
var f1 = long();
var f2 = long();
var f3 = long();
print(await f1);
print(await f2);
print(await f3);
}

你给long打了三次电话。 调用long立即开始执行其正文,该主体调用Future.delayed(Duration(seconds: 1))。 然后,long向调用方返回Future

因此,对应于f1f2f3Future的异步操作都已启动,因此它们都将在 1 秒后完成。 注意Future.wait基本上做同样的事情,只是Future.waitFutures 的Iterable上运行,而不是固定数量的 s。Future.wait没有什么根本性的魔力。

相反,当您执行以下操作时:

void inSequence() async {
// sequentially wait on the futures - takes 3 seconds
print(await long());
print(await long());
print(await long());
}

在上一个操作完成之前不要调用long()(因此不要调用Future.delayed),因此异步调用都将序列化。


1隔离中的 Dart 代码在单个线程中执行。 如果执行两个异步操作,但每个操作都在主 Dart 线程中执行其所有工作,则将它们与Future.wait一起使用将并发执行,但不能并行执行。 例如:

Future<void> spin(Duration duration) async {
await Future.delayed(Duration.zero);
var endTime = DateTime.now().add(duration);
while (DateTime.now().isBefore(endTime)) {}
}
void main() async {
var oneSecond = const Duration(seconds: 1);
var stopwatch = Stopwatch()..start();
await Future.wait([spin(oneSecond), spin(oneSecond)]);
print(stopwatch.elapsed.inSeconds); // Prints: 2
}

在上面的代码中,spin执行一个占用主线程 1 秒的同步循环,但来自两个调用的循环无法同时执行。

但是,这并不意味着某些异步操作不能并行发生。 在大多数情况下,异步操作最终会执行一些使用操作系统线程实现的较低级别的操作(例如在 Dart VM/运行时中)。 或者,您可能执行执行异步 I/O 的文件系统操作。 这取决于异步操作的实现。 如果要允许异步操作并行发生(如果可能),则通常应使用Future.wait

最新更新