“This is the 23rd day of my participation in the Gwen Challenge in November. Check out the details: The Last Gwen Challenge in 2021”

We have explained how to use Future and scheduleMicrotask in previous articles, but we found that both are synchronous and their execution order is determinable. Next we’ll look at asynchronous multithreading in Dart;

Isolate

Let’s look at the result of a code execution:

void testIsolate() {
  print('1');
  Future(() => print('3'));
  sleep(const Duration(seconds: 2));
  print('2');
}
Copy the code

According to our previous learning, we can know that the print result is 1, 2, 3. Let’s look at the print result:

According to the printed results, our conclusions are indeed verified. But obviously, even if we block the main thread, tasks in the Future are still not executed first; So is there a way to print 3 first if the main thread is blocked? This is when the multithreaded function of the Flutter is used;

We modify the code as follows:

void testIsolate() {
  print('1');
  Isolate.spawn(func, 10);
  sleep(const Duration(seconds: 2));
  print('2');
}

func (int count) => print('3');
Copy the code

At this point, let’s look at the print:

As you can see, we blocked the main thread. Even if 2 was not printed at the first time, the output of 3 was not affected. In other words, the main thread of the Isolate was not blocked. So is Isolate really a child thread?

To verify that the Isolate is on child threads, we change the code as follows:

void testIsolate() {
  print('1');
  Isolate.spawn(func, 10);
  Isolate.spawn(func2, 10);
  Isolate.spawn(func, 10);
  Isolate.spawn(func2, 10);
  Isolate.spawn(func, 10);
  Isolate.spawn(func2, 10);
  sleep(const Duration(seconds: 2));
  print('2');
}

func (int count) {
  print('The first');
}

func2 (int count) {
  print('Second');
}
Copy the code

Execution Result:

You can see that the print order is no longer fixed but random, proving that the Isolate is really on child threads;

The independent memory space of the Isolate

Note that multithreading in Dart doesn’t just open up a ‘thread’. The Isolate here is more like a process than a thread, because the Isolate has independent memory (mostly objects/data it creates). This means that the data between each Isolate is independent and there is no problem of resource grabbing. Therefore, there is no need to use locks to access data.

Let’s look at a piece of code:

void testIsolate() {
  print('1');
  Isolate.spawn(func, 100);
  sleep(const Duration(seconds: 2));
  print('a = $a');
  print('2');
}

int a = 10;

func (int count) {
  a = count;
  print(A: in the 'func$a');
}
Copy the code

In this code, we defined a variable A, the default value is 10, and then we assigned the value of count to A in the FUNc method. Then in the Isolate, we passed the value of 100 to the func method, so according to the normal logic, we should print a = 100 after the main thread blocked for 2 seconds. So is that the case? Let’s take a look at the print:

We saw that even though we assigned a to 100 in the FUNc method, a was still 10 in the later printing. This meant that the resources in the Isolate were not robbed, it was independent.

Obtain modified data in the Isolate

However, we do need to change the value of A in the child thread at some point, so how do we do that? We modify the code as follows:

void testIsolate() {
  print('1');

  // Create an interface port
  ReceivePort port = ReceivePort();
  // Create an Isolate
  Isolate.spawn(func, port.sendPort);
  // Use port to listen for data changes
  port.listen((message) {
    a = message;
    print('port on a =$a');
  });

  sleep(const Duration(seconds: 2));
  print('a = $a');
  print('2');
}

int a = 10;

func (SendPort sendPort) {
  sendPort.send(100);
  print(A: in the 'func$a');
}
Copy the code

At this point, let’s look at the results again:

We can see that at this point we can listen that A has been modified;

In this process, we did four things:

  • Create an interface:ReceivePort;
  • To create aIsolate;
  • Created withReceivePortTo listen for changes in data;
  • inIsolateAssociated methodsSendPortSending messages to transfer values;

Through the above four steps, we can modify the data inside the Isolate and obtain new values.

However, it should be noted that when we use this way to open monitoring data, it is equivalent to opening up space, so we need to manage the destruction of our own; The final code is as follows:

void testIsolate() async {
  print('1');

  // Create an interface port
  ReceivePort port = ReceivePort();
  // Create an ISOLATE
  Isolate isolate =  await Isolate.spawn(func, port.sendPort);
  // Use port to listen for data changes
  port.listen((message) {
    a = message;
    print('port on a =$a');
    / / close the port
    port.close();
    / / destroy isolate
    isolate.kill();
  });

  sleep(const Duration(seconds: 2));
  print('a = $a');
  print('2');
}

int a = 10;

func (SendPort sendPort) {
  sendPort.send(100);
  print(A: in the 'func$a');
}
Copy the code

We need to close the ReceivePort and destroy the Isolate.

Note that the Isolate is multithreaded, so “await” here does not cause future-like wait effects in subsequent code;

compute

In addition to the Isolate, there is a compute that encapsulates the Isolate in a Flutter. Let’s look at a piece of code:

Let’s look at this code in action:

We know from the execution that compute is also executed on child threads;

Compute and Isolate are different, however. Compute can receive the return value of task func. We modify the code as follows:

View the run result:

You can see that we received the return value of the func method;

Note that await of compute causes the same wait effect as in the Future;

So is compute data isolated?

We make the following changes to the code:

Running results:

You can see that compute receives the data returned from the task, but changes to the global variable A inside the task are still not available outside the task.