flutterdartdart-isolates

Access variables in external scope when using isolation in Dart


In Isolates, I can refer to a local variable from an outer scope or a field variable of a class without passing it as a separate message.
Is this implicitly copying the values into the new isolation's memory area?
I'm curious about the details.

Example

class Person {
  Person(this._baseNum);

  /// access [_baseNum] in isolate
  final int _baseNum;
  int age = 0;

  /// access [extraAge] in isolate
  Future<void> addAge(int extraAge) async {
    final mainReceivePort = ReceivePort();

    await Isolate.spawn((SendPort sendPort) async {
      sendPort.send(await _calcAge(_baseNum, extraAge));
    }, mainReceivePort.sendPort);

    age = await mainReceivePort.first;
    mainReceivePort.close();
  }

  static Future<int> _calcAge(int someNum, int age) async {
    // ... heavy work ...
    return age + someNum;
  }
}

// ...

void main() {
  test('test', () async {
    final p = Person(10);
    await p.addAge(3);
    expect(p.age, 13);
  });
}

Solution

  • In Isolates, I can refer to a local variable from an outer scope or a field variable of a class without passing it as a separate message.

    Is this implicitly copying the values into the new isolation's memory area?

    Yes it is.

    One way to demonstrate this is if you take one of these variables from an outer scope or field variable, and update the value within the isolate. What you will see is that from outside the isolate, the value will not be updated. This is because they are working with independent copies of the variable.

    import 'dart:isolate';
    import 'package:test/test.dart';
    
    class Person {
      Person(this._baseNum);
    
      /// access [_baseNum] in isolate
      int _baseNum;
      int age = 0;
    
      /// access [extraAge] in isolate
      Future<void> addAge(int extraAge) async {
        final mainReceivePort = ReceivePort();
    
        await Isolate.spawn((SendPort sendPort) async {
          _baseNum++; // modify _baseNum
          sendPort.send(await _calcAge(_baseNum, extraAge));
        }, mainReceivePort.sendPort);
    
        age = await mainReceivePort.first;
        mainReceivePort.close();
      }
    
      static Future<int> _calcAge(int someNum, int age) async {
        // ... heavy work ...
        return age + someNum;
      }
    }
    
    // ...
    
    void main() {
      test('test', () async {
        final p = Person(10);
        await p.addAge(3);
        expect(p.age, 14);
        expect(p._baseNum, 10); // _baseNum still 10 despite _baseNum++ in isolate
      });
    }