王尘宇王尘宇

研究百度干SEO做推广变成一个被互联网搞的人

Flutter第十七章(Isolate 并发)

模拟1.gif

可以看到点击计算按钮后很快就计算出了结果值,当然这是绝对理想的状态下。因为我们的计算并不复杂即使在主线程中计算也并未感知耗时的过程。那么现在将100 调整到 10000000000后来看下表现呢?

模拟2.gif

在修改计算值后再次点击计算按钮,整个应用是直接卡死掉了。在实际开发你的耗时操作可能不是这么一个计算,可能更多的是处理网络上的一些请求或处理结果等,然而类似的这样题应该是放到隔离器中去处理。

三、怎么创建 Isolate?

还是先回到源码中去,在源码中有这么一个派生函数:

      /**
       * Creates and spawns an isolate that shares the same code as the current
       * isolate.
       *
       * The argument [entryPoint] specifies the initial function to call
       * in the spawned isolate.
       * The entry-point function is invoked in the new isolate with [message]
       * as the only argument.
       *
       * The function must be a top-level function or a static method
       * that can be called with a single argument,
       * that is, a compile-time constant function value
       * which accepts at least one positional parameter
       * and has at most one required positional parameter.
       * The function may accept any number of optional parameters,
       * as long as it *can* be called with just a single argument.
       * The function must not be the value of a function expression
       * or an instance method tear-off.
       *
       * Usually the initial [message] contains a [SendPort] so
       * that the spawner and spawnee can communicate with each other.
       *
       * If the [paused] parameter is set to `true`,
       * the isolate will start up in a paused state,
       * just before calling the [entryPoint] function with the [message],
       * as if by an initial call of `isolate.pause(isolate.pauseCapability)`.
       * To resume the isolate, call `isolate.resume(isolate.pauseCapability)`.
       *
       * If the [errorsAreFatal], [onExit] and/or [onError] parameters are provided,
       * the isolate will act as if, respectively, [setErrorsFatal],
       * [addOnExitListener] and [addErrorListener] were called with the
       * corresponding parameter and was processed before the isolate starts
       * running.
       *
       * If [debugName] is provided, the spawned [Isolate] will be identifiable by
       * this name in debuggers and logging.
       *
       * If [errorsAreFatal] is omitted, the platform may choose a default behavior
       * or inherit the current isolate's behavior.
       *
       * You can also call the [setErrorsFatal], [addOnExitListener] and
       * [addErrorListener] methods on the returned isolate, but unless the
       * isolate was started as [paused], it may already have terminated
       * before those methods can complete.
       *
       * Returns a future which will complete with an [Isolate] instance if the
       * spawning succeeded. It will complete with an error otherwise.
       */
      external static Future<Isolate> spawn<T>(
          void entryPoint(T message), T message,
          {bool paused: false,
          bool errorsAreFatal,
          SendPort onExit,
          SendPort onError,
          @Since("2.3") String debugName});

参数作用:

①、entryPoint(必传): 传入的是一个函数,该函数指定要在派生的 isolate中调用的初始函数。这个函数必须是可以以单一参数调用的全局函数或静态方法,否则报错。

②、message(必传): 携带 SendPort的初始化消息,以便不同的 隔离区之间通信。

③、paused (非必传):如果指定pause为 true,则会在 message 调用 entryPoint函数之前将 isolate以暂停状态启动。如果需要恢复隔离器的状态使用
isolation.resume(isolation. pausecapability)即可。

④、errorsAreFatal(非必传):如果指定该参数为 true。则在 isolate中的 addOnExitListener 和 addErrorListener 监听事件中得到响应的退出和异常信息。

⑤、onExit、onError(配合 参数 ④一起使用)。

⑥、debugName(非必传),如果设置了该参数,在控制台相应的日字信息会通过该标识显示。

再次说明:其中 entryPoint 参数一定是一个静态函数或者全局函数,message 参数则是由 ReceivePort 创建的 SendPort,通过 receivePort.sendPort方式可以拿到。
下面来用get到的新技能将上面会卡死程序的计算改造下:
    import 'dart:isolate';

    import 'package:flutter/cupertino.dart';
    import 'package:flutter/material.dart';
    import 'package:flutter_learn/page/dialog/LoadingViewDialog.dart';

    class IsolatePage extends StatefulWidget{

      @override
      State<StatefulWidget> createState() {
        return IsolatePageState();
      }
    }


    class IsolatePageState extends State<IsolatePage> {

      var content = "计算中...";


      static Future<dynamic> calculation(int n) async{

        //首先创建一个ReceivePort,因为创建isolate所需的参数,必须要有SendPort,SendPort需要ReceivePort来创建
        final response = new ReceivePort();
        //开始创建isolate,createIsolate是创建isolate必须要的参数。
        Isolate isolate = await Isolate.spawn(createIsolate,response.sendPort);

        //获取sendPort来发送数据
        final sendPort = await response.first as SendPort;
        //接收消息的ReceivePort
        final answer = new ReceivePort();
        //发送数据
        sendPort.send([n,answer.sendPort]);
        //获得数据并返回
        return answer.first;
      }

       //创建isolate必须要的参数
      static void createIsolate(SendPort initialReplyTo){
        final port = new ReceivePort();
        //绑定
        initialReplyTo.send(port.sendPort);
        //监听
        port.listen((message){
          //获取数据并解析
          final data = message[0] as num;
          final send = message[1] as SendPort;
          //返回结果
          send.send(sum(data));
        });

      }

      //计算0到 num 数值的总和
      static num sum(int num) {
        int count = 0;
        while (num > 0) {
          count = count+num;
          num--;
        }
        return count;
      }

      @override
      Widget build(BuildContext context) {


        return Scaffold(

          ///FloatingActionButton
          floatingActionButton: FloatingActionButton(
            elevation: 0,
            child: Text('计算'),
            onPressed: () {
              calculation(100000000).then((onValue){
                setState(() {
                  content = "总和$onValue";
                  print("计算结果:$onValue");
                });
              });
            },
          ),
          floatingActionButtonLocation: FloatingActionButtonLocation.endFloat,

          appBar: AppBar(
            title: Text("Isolate"),
          ),

          body: SafeArea(
              child:Center(
                child: Column(
                  children: <Widget>[
                    Container(
                      width: double.infinity,
                      height: 400,
                      //前面章节中的自定义View
                      child: LoadingViewDialog(
                        dialogWidth: double.infinity,
                          //调用对话框
                        progress: CircularProgressIndicator(
                          strokeWidth: 3,
                          //背景颜色
                          backgroundColor: Colors.red,
                        ),

                        content: Text(
                          content,
                          style: TextStyle(color: Colors.blue),
                        ),
                        maxShowTime: 10000,
                      )
                    ),
                  ],
                ),
              ),
          ),
        );
      }
    }

运行效果:

Isolate模拟3.gif

可以看到再次点击计算按钮后,从加载圈圈就可以看出。整个程序照常运行,依然纵享湿滑。完美解决了上面的卡死现象。

四、Isolate的暂停,恢复,以及关闭

    //恢复 isolate 的使用
    isolate.resume(isolate.pauseCapability);

    //暂停 isolate 的使用
    isolate.pause(isolate.pauseCapability);

    //结束 isolate 的使用
    isolate.kill(priority: Isolate.immediate);

    //赋值为空 便于内存及时回收
    isolate = null;

你可能会问有没有更简单的方式啊? Isolate写起来好麻烦。确实是有的,由于Dart 中的Isolate确实比较重量级,因此Flutter为了简化用户代码,在foundation库中封装了一个轻量级compute操作,但其内部仍然还是 Isolate 在工作,只是 Flutter 在框架层帮我们做了简易优化。

五、compute 的使用

尽管compute在使用上很简单,也能处理耗时操作。但它的使用还是有很多限制的,它没有办法多次返回结果,也没有办法持续性的传值计算,每次调用,相当于新建一个隔离,如果调用过多的话反而会适得其反。这往往根据实际开发中业务情况来取决,如果只是单纯的处理几次耗时工作可以使用 compute,如果是需要一个任务来长时间的处理耗时类工作 则需要使用 Dart 中的 Isolate。

接下来使用 compute 简化上面的代码,修改 FloatingActionButton按钮 点击后使用 compute 方式来处理:

  ///FloatingActionButton
  floatingActionButton: FloatingActionButton(
    elevation: 0,
    child: Text('计算'),
    onPressed: () {

      compute(sum,100000000).then((onValue){
        setState(() {
          content = "总和$onValue";
          print("计算结果:$onValue");
        });
      });
    },
  ),

效果如下:

compute模拟4.gif

效果和直接使用 Isolate 处理一样,纵享湿滑 哈哈O(∩_∩)O

温馨提示: compute 所传入的函数和 Isolate 一样 必须是可以以单一参数调用的全局函数或静态方法,否则会报错。

本章节对 Isolate 的基本招式就打完了,更多高深秘法需要深入内部自行修炼了。

好了本章节到此结束,又到了说再见的时候了,如果你喜欢请留下你的小红星;你们的支持才是创作的动力,如有错误,请热心的你留言指正, 谢谢大家观看,下章再会 O(∩_∩)O

实例源码地址:https://github.com/zhengzaihong/flutter_learn/blob/master/lib/page/isolate/IsolatePage.dart

相关文章

评论列表

发表评论:
验证码

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。