• Flutter:多线程Isolate的简单使用


    在flutter中如果要使用线程,需要借助Isolate来实现。

    简介

    在Flutter中,Isolate是一种轻量级的线程解决方案,用于在应用程序中执行并发任务。Isolate可以被认为是独立于主线程的工作单元,它们可以在后台执行任务而不会阻塞应用程序的用户界面。

    Isolate提供了多线程编程的能力,允许开发者在应用程序中同时执行多个任务,从而提高应用程序的性能和响应能力。每个Isolate都有自己独立的内存空间和执行环境,它们之间可以通过消息传递进行通信。

    在Flutter中,可以使用dart:isolate库来创建和管理Isolate。通过创建一个Isolate对象,可以指定要执行的任务代码。Isolate可以执行耗时的计算、网络请求、文件操作等任务,而不会阻塞应用程序的主线程。

    与其他线程解决方案相比,Isolate的一个重要特点是它们之间的内存是隔离的,这意味着每个Isolate都有自己独立的内存空间,它们之间不能直接共享数据。为了在Isolate之间传递数据,可以使用消息传递机制,即将数据打包成消息发送给目标Isolate,并在接收端解包处理。

    使用Isolate可以提高应用程序的性能和响应能力,特别是在处理大量计算密集型任务或需要与外部资源进行交互的场景中。然而,需要注意的是,Isolate的创建和销毁都会带来一定的开销,因此在使用Isolate时需要权衡资源消耗和性能提升之间的平衡。

    总而言之,Isolate是Flutter中的一种轻量级线程解决方案,用于在应用程序中执行并发任务。它们可以独立于主线程执行任务,并通过消息传递机制进行通信。使用Isolate可以提高应用程序的性能和响应能力,特别是在处理计算密集型任务或需要与外部资源进行交互的场景中。

    匿名函数使用

    void _incrementCounter() {
      setState(() {
        _counter++;
      });
      // 匿名线程
      Isolate.spawn((message) {
        print("匿名函数线程:$message");
      }, '哈哈哈');
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    参数会被传递到匿名函数中,并被匿名函数所使用。

    普通函数

     void _incrementCounter() {
       setState(() {
         _counter++;
       });
       // 普通线程
       Isolate.spawn(newThread1, "普通线程");
     }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    // 创建一个额外线程
    void newThread1(String message){
      print(message);
    }
    
    • 1
    • 2
    • 3
    • 4

    这个一定要注意,在flutter中使用时newThread1不能写在主进程所在的类里。否则会提示[ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: Invalid argument(s): Illegal argument in isolate message: object is unsendable - Library:'dart:async' Class: _AsyncCompleter@4048458 (see restrictions listed at SendPort.send() documentation for more information)

    子线程向主线程通信

    // 创建一个额外线程
    void newThread1(SendPort mainThreadPort) {
      int num = 0;
      Timer.periodic(const Duration(seconds: 1), (timer) {
        // 每隔1秒num加1
        num += 1;
        mainThreadPort.send(num);
        if (num == 10) {
          // 向主进程发消息执行完成
          mainThreadPort.send('stop');
        }
      });
    }
    
    class _MyHomePageState extends State<MyHomePage> {
      final receivePort = ReceivePort();
    
      void _incrementCounter() async {
        // 子线程
        final isolate = await Isolate.spawn(newThread1, receivePort.sendPort);
    
        // 监听子线程发送的消息
        receivePort.listen((message) {
          print("子线程发送的消息:$message");
          if (message == 'stop') {
            // 执行完成则停止显示
            print("线程执行完成");
            isolate.kill(priority: Isolate.immediate);
          }
        });
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            backgroundColor: Theme.of(context).colorScheme.inversePrimary,
            title: Text(widget.title),
          ),
          body: const Center(child: Text("多线程")),
          floatingActionButton: FloatingActionButton(
            onPressed: _incrementCounter,
            tooltip: 'Increment',
            child: const Icon(Icons.add),
          ), // This trailing comma makes auto-formatting nicer for build methods.
        );
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48

    在这里插入图片描述
    主线程向子线程通信
    在子线程执行期间,主线程也可以向子线程进行通信。基于上面的代码,进行简单修改

    void newThread1(SendPort mainThreadPort) {
      int num = 0;
      ReceivePort receivePort = ReceivePort();
      Timer.periodic(const Duration(seconds: 1), (timer) {
        // 每隔1秒num加1
        num += 1;
        mainThreadPort.send(num);
        if(num==1){
          // 跟主进程类似,将receivePort.sendPort传递给主进程,主进程才能向子进程通信
          mainThreadPort.send(receivePort.sendPort);
        }
        if (num == 10) {
          // 向主进程发消息执行完成
          mainThreadPort.send('stop');
        }
      });
      // 接收主进程发送的消息
      receivePort.listen((message) {
        print("收到了主进程的消息:$message");
      });
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
      void _incrementCounter() async {
        // 子线程
        final isolate = await Isolate.spawn(newThread1, receivePort.sendPort);
        late SendPort childSendPort;
    
        // 监听子线程发送的消息
        receivePort.listen((message) {
          print("子线程发送的消息:$message");
          if (message is SendPort) {
            childSendPort = message;
          }
          if (message == 6) {
            childSendPort.send("加油,马上完成了");
          }
          if (message == 'stop') {
            // 执行完成则停止显示
            print("线程执行完成");
            isolate.kill(priority: Isolate.immediate);
          }
        });
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    怎么理解呢?
    子线程要想向主线程通信,需要获取到主线程的receivePort.sendPort,因此在子线程已创建,就以参数的形式将主线程的receivePort.sendPort传递给子线程。
    同理,主线程要想向子线程通信也需要拿到子线程的receivePort.sendPort,因此在子线程一开始执行就将自己的receivePort.sendPort发送给主线程。

    只有主线程、子线程都拿到对方的receivePort.sendPort 才可以进行互相通信。
    在这里插入图片描述

  • 相关阅读:
    容器内存指标
    Interval Envision图像库,非常强大的图像功能
    Linux多线程(线程池与单例模式)
    【MySQL】 MySQL的内置函数——日期函数、字符串函数、数学函数、聚合函数、其他函数
    【JavaEE---复习】一、.Spring的Ioc和DI
    【网络层介绍】
    MASM 64汇编
    grafana、prometheus监控linux、mysql等
    mybatis学习(2):基本设置和核心配置
    HTML5的基础知识的梳理及常用标签
  • 原文地址:https://blog.csdn.net/weixin_41897680/article/details/134528685