Flutter & Dart:管理从 Isolate 中进行的计算中获取的数据的正确方法是什么?

Ivá*_*oed 5 dart dart-isolates flutter

当在 Dart 中进行昂贵的计算时,强烈建议启动额外的isolate。我知道,由于隔离不共享任何状态,如果您想在它们之间创建通信,则可以使用SendPortReceivePort将消息从一个隔离传递到另一个隔离隔离。然而,当在另一个 Isolate 中进行计算时,在这个新创建的isolate 的过程中,实际上可以初始化一个已经创建的变量,例如 Singleton 的变量,对吧?

所以我的问题是:这样继续下去可以吗?管理从隔离中进行的计算中获得的数据的正确方法是什么?为什么?在新隔离区中进行计算时,我们是否应该始终只处理消息?

Chr*_*ore 6

不可以,您不能从一个不同的隔离株中初始化一个隔离株的变量。正如您提到的那样,他们没有共享内存。隔离之间通信的唯一方法是通过端口。

即使分析器可能允许您修改一个隔离中的变量与另一个隔离中的变量,您也不会得到您期望的行为。分离可以被认为是完全不同的过程。

我做了一个例子来展示这种行为:

import 'dart:isolate';

Future<void> main(List<String> arguments) async {
  final single = Singleton();
  print(single.variable);

  single.variable = 1;
  print(single.variable);

  final port = ReceivePort();
  final isolate = await Isolate.spawn(isolateWork, port.sendPort);
  await for(dynamic message in port) {
    if(message == 'done') {
      isolate.kill();
      break;
    }
  }
}

void isolateWork(SendPort port) {
  final single = Singleton();
  print('In isolate: ${single.variable}');
  port.send('done');
}

class Singleton {
  int variable = 0;

  static final Singleton _singleton = Singleton._internal();

  factory Singleton() {
    return _singleton;
  }

  Singleton._internal();
}
Run Code Online (Sandbox Code Playgroud)

这是一个简单的程序,尝试使用单例来修改一个隔离中与衍生隔离中的变量。如果存在共享内存,当我第一次获取单例时,可能会期望以下输出,打印variable的默认值0,更改variable1,然后variable在隔离中打印:

0
1
In isolate: 1
Run Code Online (Sandbox Code Playgroud)

但是,由于这些隔离是完全独立的进程,因此生成的隔离有其自己的单例实例,从而导致以下输出:

0
1
In isolate: 0
Run Code Online (Sandbox Code Playgroud)

0并按1预期在主隔离中打印,但生成的有自己的内存并使用单独的单例对象,因此0即使它1位于主隔离中也会打印。


正如您在上面的示例中看到的,我确实使用了端口,以便我的程序可以检测隔离何时完成并优雅地终止进程。使用端口传递消息是在隔离之间传递数据的正确且唯一的方法。


您将这个问题标记为,所以我猜您正在使用 Flutter 。如果您正在使用单个输出进行单个大型计算,那么使用 Flutter 的compute方法可能是使用隔离的最简单方法,因为它消除了您使用端口的需要。然而,它可能是有限制的,并且像我上面那样使用完整的隔离实现通常很有用。