flutter "multithreading" isolate primary level understanding

Keywords: Flutter

We learned in the second grade of primary school:

Dart is single threaded and fluent depends on dart.

However, if we want to do some operations with a large amount of computation in the main thread, it will inevitably block the thread and make the UI update stuck or even stuck. Then what shall I do?

The good news is that Dart provides us with an isolate, which is similar to a thread. It is a thread in Dart.

The difference between an isolate and a thread is that memory is shared between threads, but not between an isolate and an isolate, so it is called an isolate.

In the flutter, the main thread is the main isolate. If we want to perform some operations with a large amount of computation, we should start a new isolate.

So how to open it? Before that, I want to tell a story.

The story of little red and little blue

There is a dancer named Xiao Hong. She is dancing to the audience

But the audience asked her to count how many even numbers are in a number while dancing. therefore...

That's OK! You must count while you jump. You can't stop when you count!

So little red had no choice but to call a little blue to help her calculate in the different world.

 

But little red and little blue are separated by the barriers of different worlds, and they don't have the same super power of thought. You can only send a package to Xiaolan when summoning.

Xiaolan receives the package after being called out. After opening it, it is the number to be calculated and starts to calculate. But how can she tell Xiaohong the result after calculation?

God made an agreement that when Xiaohong calls Xiaolan, a conveyor will be changed (the conveyor can be used to receive packages and generate an exclusive transmitter). Then send the transmitter to Xiaolan.

When Xiaolan is summoned, open the package. There is a transmitter inside. Then Xiaolan changes itself into a conveyor to generate a transmitter, and then sends Xiaolan's transmitter to Xiaohong with Xiaohong's transmitter. After sending out, sit next to the conveyor and wait for the package.

When Xiaohong receives Xiaolan's transmitter, she saves Xiaolan's transmitter.

When the audience asks Xiaohong to calculate, they are distracted and dance while generating a temporary conveyor. The number to be calculated and the temporary transmitter are packaged into a package, and then sent to Xiaolan through Xiaolan's transmitter, waiting for the conveyor to produce the result. Because you don't have to do it yourself, just wait, so the lines are smooth and the movements are beautiful when dancing.

Back to Xiaolan, Xiaolan sees a package on the conveyor, which is a temporary transmitter and a number. So Xiao Lan began to calculate. After calculation, use the temporary transmitter to send the number to Xiaohong.

After receiving the result, Xiao Hong tells the audience how many even numbers that number has.

At the end of the story, I tried this style for the first time. It may be a little bad, but it should be very easy to understand in combination with the code.

Code practice

First, let's let Xiao Hong dance.

  @override
  void initState() {
    controller =
        AnimationController(duration: Duration(seconds: 3), vsync: this);
    animation = Tween<double>(begin: 0, end: pi * 2).animate(controller);
    controller.repeat();
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            AnimatedBuilder(
                animation: animation,
                child: Text(
                  'Xiao Hong',
                  style: TextStyle(fontSize: 30, color: Colors.red),
                ),
                builder: (context, child) {
                  return Transform.rotate(
                    angle: animation.value,
                    child: child,
                  );
                }),
          ],
        ),
      ),
    );
  }

Next, let Xiao Hong calculate how many even numbers there are in a number.

@override
Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(
      title: Text(widget.title),
    ),
    body: Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          AnimatedBuilder(
              animation: animation,
              child: Text(
                'Xiao Hong',
                style: TextStyle(fontSize: 30, color: Colors.red),
              ),
              builder: (context, child) {
                return Transform.rotate(
                  angle: animation.value,
                  child: child,
                );
              }),
          Padding(
            padding: EdgeInsets.only(top: 16),
            child:
                RaisedButton(onPressed: count, child: Text('Asynchronously calculate the number of even numbers')),
          ),
          Text(result)
        ],
      ),
    ),
  );
}

int getRandom() {
  int a =  Random().nextInt(100);
  return a + 1000000000;
}

// Asynchronous computing
count() async {
  int random = getRandom();
  int r = countEven(random);
  setState(() {
    this.result = '${random.toString()}yes ${r.toString()}Even number';
  });
}

//Calculate the number of even numbers
int countEven(num num) {
  int count = 0;
  while (num > 0) {
    if (num % 2 == 0) {
      count++;
    }
    num--;
  }
  return count;
}

This is the effect


 

Define isolate

I'd call it summoning little blue.

First, we need to know two classes:

ReceivePort
SendPort

ReceivePort is the transmitter in the story, and SendPort is the transmitter.

We can create the conveyor and the corresponding transmitter in the following ways

ReceivePort receive = ReceivePort();
SendPort sender = receive.sendPort;

OK, just know that. Next, we define little blue.

// Message package, used to store temporary senders and messages
class MessagePackage {
  SendPort sender; // Temporary transmitter
  dynamic msg; // news

  MessagePackage(this.sender, this.msg);

}

// I'm Xiao Lan. I'm responsible for calculating the number of even numbers. I must be a top-level function
blueCounter(SendPort redSendPort) {
  // Create a conveyor for little blue
  ReceivePort blueReceivePort = ReceivePort();
  // Use the little red transmitter to send the little blue transmitter
  redSendPort.send(blueReceivePort.sendPort);
  // Monitor Xiaolan's conveyor and wait for Xiaohong to call Xiaolan for calculation
  blueReceivePort.listen((package) {
    // msg here is dynamic and needs to be converted into MessagePackage class, which is the package encapsulation class defined above
    MessagePackage _msg = package as MessagePackage;
    // Xiaolan starts to calculate
    int r = countEven(_msg.msg as num);
    // After calculation, use Xiaohong's temporary transmitter to tell Xiaohong
    _msg.sender.send(r);
  });
}

Create isolate

The tool man Xiaolan has been defined. Let's initialize (summon) Xiaolan.

// Create isolate
void createIsolate() async {
  // Create a small red receiver to receive a small blue transmitter
  ReceivePort redReceive = ReceivePort();
  // Create an isolate and pass Xiaohong's transmitter to Xiaolan
  isolate = await Isolate.spawn<SendPort>(blueCounter, redReceive.sendPort);
  // Wait for Xiaolan to send the transmitter to Xiaohong
  blueSender = await redReceive.first;
  // No, remember to turn off the receiver
  redReceive.close();
}


@override
void initState() {
  controller =
      AnimationController(duration: Duration(seconds: 3), vsync: this);
  animation = Tween<double>(begin: 0, end: pi * 2).animate(controller);
  controller.repeat();
  // Initialize isolate in initState
  createIsolate();
  super.initState();
}

Now Xiaolan has been summoned and has established communication with Xiaohong.

Start isolate calculation

Next, let's let Xiao Hong start calculating.

@override
Widget build(BuildContext context) {
  ...
  Padding(
      padding: EdgeInsets.only(top: 16),
      child: RaisedButton(
          onPressed: isolateCount, child: Text('isolate Calculate the number of even numbers')
      ),
  ),
  ...
}

// Enable isolate calculation
isolateCount() async {
  // Gets the number to calculate
  int random = getRandom();
  // Create a temporary conveyor
  ReceivePort _temp = ReceivePort();
  // Send a message package with the small blue transmitter, which is the transmitter of the temporary conveyor and the number to be calculated
  blueSender.send(MessagePackage(_temp.sendPort, random));
  // Wait for the temporary conveyor to return the calculation result
  int r = await _temp.first;
  // No, remember to turn off the temporary receiver
  _temp.close();
  // Tell the audience the results of the calculation
  setState(() {
    this.result = '${random.toString()}yes ${r.toString()}Even number';
  });
}

It should be noted that when you finish using the isolate, remember to destroy it.

@override
void dispose() {
  // Destroy isolate
  isolate?.kill(priority: Isolate.immediate);
  super.dispose();
}

OK, here I believe you have understood and can use isolate.
Let's take a look at the renderings.

Using computed

It's not over yet. Maybe you'll find it too troublesome. Yes, it's too troublesome to use isolate. Isolate is designed to input and output multiple times, and we only have one input and output for this calculation, so we can complete the calculation operation with the computed provided by fluent, which is a package of isolate. Let's see how to use it! Knock simple.

import 'dart:isolate';

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'dart:math';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'isolate Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'isolate Demo'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage>
    with SingleTickerProviderStateMixin {
  AnimationController controller;
  Animation<double> animation;
  String result = '';
  SendPort blueSender;
  Isolate isolate;

  @override
  void initState() {
    controller =
        AnimationController(duration: Duration(seconds: 3), vsync: this);
    animation = Tween<double>(begin: 0, end: pi * 2).animate(controller);
    controller.repeat();
    // Initialize isolate in initState
    createIsolate();
    super.initState();
  }

  @override
  void dispose() {
    // Destroy isolate
    isolate?.kill(priority: Isolate.immediate);
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            AnimatedBuilder(
                animation: animation,
                child: Text(
                  'Xiao Hong',
                  style: TextStyle(fontSize: 30, color: Colors.red),
                ),
                builder: (context, child) {
                  return Transform.rotate(
                    angle: animation.value,
                    child: child,
                  );
                }),
            Padding(
              padding: EdgeInsets.only(top: 16),
              child: RaisedButton(onPressed: count, child: Text('Asynchronously calculate the number of even numbers')),
            ),
            Padding(
              padding: EdgeInsets.only(top: 16),
              child: RaisedButton(
                  onPressed: isolateCount, child: Text('isolate Calculate the number of even numbers')),
            ),
            Padding(
              padding: EdgeInsets.only(top: 16),
              child: RaisedButton(
                  onPressed: computeCount, child: Text('compute Calculate the number of even numbers')),
            ),
            Text(result)
          ],
        ),
      ),
    );
  }

  // Get random number
  int getRandom() {
    int a = Random().nextInt(100);
    return a + 1000000000;
  }

  // Asynchronous computing
  count() async {
    int random = getRandom();
    int r = countEven(random);
    setState(() {
      this.result = '${random.toString()}yes ${r.toString()}Even number';
    });
  }

  // Create isolate
  void createIsolate() async {
    // Create a small red receiver to receive a small blue transmitter
    ReceivePort redReceive = ReceivePort();
    // Create an isolate and pass Xiaohong's transmitter to Xiaolan
    isolate = await Isolate.spawn<SendPort>(blueCounter, redReceive.sendPort);
    // Wait for Xiaolan to send the transmitter to Xiaohong
    blueSender = await redReceive.first;
    // No, remember to turn off the receiver
    redReceive.close();
  }

  // Calculate with compute
  computeCount() async {
    int random = getRandom();
    // The callback function of compute must be a top-level function or static function
    int r = await compute(countEven, random);
    setState(() {
      this.result = '${random.toString()}yes ${r.toString()}Even number';
    });
  }

  // Enable isolate calculation
  isolateCount() async {
    // Gets the number to calculate
    int random = getRandom();
    // Create a temporary conveyor
    ReceivePort _temp = ReceivePort();
    // Send a message package with the small blue transmitter, which is the transmitter of the temporary conveyor and the number to be calculated
    blueSender.send(MessagePackage(_temp.sendPort, random));
    // Wait for the temporary conveyor to return the calculation result
    int r = await _temp.first;
    _temp.close();
    // Tell the audience the results of the calculation
    setState(() {
      this.result = '${random.toString()}yes ${r.toString()}Even number';
    });
  }
}

// Message package, used to store temporary senders and messages
class MessagePackage {
  SendPort sender; // Temporary transmitter
  dynamic msg; // news

  MessagePackage(this.sender, this.msg);
}

// I'm Xiao Lan. I'm responsible for calculating the number of even numbers. I must be a top-level function
blueCounter(SendPort redSendPort) {
  // Create a conveyor for little blue
  ReceivePort blueReceivePort = ReceivePort();
  // Use the little red transmitter to send the little blue transmitter
  redSendPort.send(blueReceivePort.sendPort);
  // Monitor Xiaolan's conveyor and wait for Xiaohong to call Xiaolan for calculation
  blueReceivePort.listen((package) {
    // msg here is dynamic and needs to be converted into MessagePackage class, which is the package encapsulation class defined above
    MessagePackage _msg = package as MessagePackage;
    // Xiaolan starts to calculate
    int r = countEven(_msg.msg as num);
    // After calculation, use Xiaohong's temporary transmitter to tell Xiaohong
    _msg.sender.send(r);
  });
}

//Calculate the number of even numbers. This function requires a lot of computing resources and time
int countEven(num num) {
  int count = 0;
  while (num > 0) {
    if (num % 2 == 0) {
      count++;
    }
    num--;
  }
  return count;
}

 

 

 



Author: seven point red panda
Link: https://www.jianshu.com/p/27646ea6bcd1
Source: Jianshu
The copyright belongs to the author. For commercial reprint, please contact the author for authorization, and for non-commercial reprint, please indicate the source.

Posted by Nic on Mon, 20 Sep 2021 18:52:44 -0700