[Flutter] isolate

박재빈·2025년 2월 15일
1

1. Isolate란?

Flutter는 기본적으로 싱글 스레드(Single-threaded)모델을 사용합니다. 즉, 하나의 스레드에서 모든 작업을 처리하기 때문에 무거운 연산이 실행되면 UI가 멈출 수 있습니다. 이를 방지하기 위해 Isolate를 사용하면 메인 스레드와 독립적으로 실행되는 새로운 스레드에서 작업을 수행할 수 있습니다.

  • 일반적인 스레드와 달리 각 Isolate는 독립적인 메모리 공간을 가집니다.
  • 서로 데이터를 직접 공유할 수 없습니다.
    • SendPortReceivePort로 메시지를 주고받아야 합니다.
  • Flutter의 UI는 메인 Isolate에서 실행되므로, 무거운 연산을 별도 Isolate에서 실행하면 UI가 멈추지 않고 부드럽게 동작합니다.

2. Isolate의 동작 방식

Dart의 Isolate는 기본적으로 다음과 같은 구조로 동작합니다.
1. 메인 Isolate (UI 스레드)에서 새로운 Isolate를 생성
2. 새 Isolate가 독립적으로 실행되며, 자신만의 힙 메모리를 가짐
3. 데이터 공유는 SendPortReceivePort를 사용하여 메시지를 주고받는 방식으로 처리

3. Isolate 예제

UI 멈추는 예제

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key});

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  String result = '버튼을 눌러 계산 시작';
  bool isLoading = false;

  void _calculateHeavyTask() {
    setState(() {
      result = '계산 중...';
    });

    List<int> numbers = List.generate(5000000, (index) => index);
    List<int> squaredNumbers = numbers.map((n) => n * n).toList();

    setState(() {
      result = "계산 완료! 첫 5개: ${squaredNumbers.sublist(0, 5)}";
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            CircularProgressIndicator(),
            Text(result),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _calculateHeavyTask,
        child: Icon(Icons.calculate),
      ),
    );
  }
}

버튼을 클릭했을때 로딩 애니메이션이 결과가 나오기 전에는 멈춤

Isolate 적용

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key});

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  String result = '버튼을 눌러 계산 시작';

  ReceivePort _receivePort = ReceivePort();
  late final _isolate;

  @override
  void initState() {
    super.initState();
    _receivePort.listen((message) {
      setState(() {
        result = message;
      });

    },);
  }


  void _calculateHeavyTask() async {
    setState(() {
      result = '계산 중...';
    });


    _isolate = await Isolate.spawn(_isolateTask, _receivePort.sendPort);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            CircularProgressIndicator(),
            Text(result),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _calculateHeavyTask,
        child: Icon(Icons.calculate),
      ),
    );
  }
}

void _isolateTask(SendPort sendPort) {
  List<int> numbers = List.generate(5000000, (index) => index);
  List<int> squaredNumbers = numbers.map((n) => n * n).toList();
  sendPort.send("계산 완료! 첫 5개: ${squaredNumbers.sublist(0, 5)}");
}

Isolate를 적용한 경우에는 로딩 애니메이션이 멈추지 않음

0개의 댓글