Write before dawn

80% of the problems are multithreaded.

For a single-threaded program, only one piece of code is executing at a time, and access and changes to the state in memory occur exclusively.

but…

Modern devices are typically multi-core cpus, and for efficiency, they use threads that share content to run code concurrently.

of course…

Sharing content can lead to race conditions, which can cause errors and increase code complexity.

of course…

We can use locks to solve the problem of race conditions.

but…

The use of locks means that if a resource is in use, then subsequent callers (or threads) cannot do anything meaningful other than wait for the task. This is not acceptable even for increasingly high performance devices. Another problem with locks is that they need to be carefully designed. Individual locks are fine, but as more locks are added, deadlock can occur.

Optimistic lock, pessimistic lock are you afraid of?

so…

The Isolate emerged.

Flutter in the Isolate

Before we talk about Isolate, let’s talk a little bit about the asynchronism of Flutter.

There are generally two ways to implement asynchrony: one is multi-threaded, and the other is an event-based asynchronous model. Multi-threaded we don’t mention, the asynchronous model based on event is simply a single thread in an event loop and an event queue, events cycle continuously removed from the event queue to perform, when the loop in a time-consuming event, it will not stop to wait for, but will skip the event continued to perform, when not finished time-consuming event handling, Go back and look at the results of time-consuming events. Thus, time-consuming events do not block the loop, and events after time-consuming events have a chance to be executed.

It is easy to find that this event-based asynchronous model is suitable for I/O intensive time-consuming operations, because I/O time-consuming operations tend to waste time waiting for data to be transmitted or results to be returned, so this asynchronous model is often used for network server concurrency. If you are computationally intensive, you should take advantage of the processor’s multiple cores as much as possible and implement parallel computing.

What does this have to do with Isolate?

The main function of Flutter is wrapped in an isolation domain called the main Isolate. Each Isolate contains an independent memory, an event loop, an event loop queue, and a thread that executes the event loop.

If we do not open a new Isolate, by default all code will run in the main Isolate, which only corresponds to one thread. This is one reason why Flutter is a single thread.

Memory is isolated and objects cannot be accessed directly. Therefore, there is no problem of sharing resources. Therefore, there is no need to consider the headache of multi-threading.

IOS has a similar concept: actors.

Of course, the Isolate is able to communicate with each other through messaging.

but…

Not all objects meet the delivery criteria, and if they do not, the message will fail to be sent.

An 🌰…

If you want to send a List, you need to make sure that all elements in the List are passable. Suppose there is a Socket in the list, and since it cannot be passed, you cannot send the whole list.

and…

Blocking one Isolate does not affect other isolates.

so…

You can see that the Isolate is similar in concept to threads and processes, except that each Isolate has separate memory and separate threads that run event loops.

A 🌰

Let’s write a Demo to see the difference between using Isolate and using single-threaded asynchronism:

The above button used the Isolate to perform time-consuming operation, which had almost no impact on THE UI, while the following button performed the same operation, but instead of using the Isolate, it directly used async/await, and the UI was directly stuck.

How to use

There are three ways:

  • Dart – Isolatestatic Future<Isolate> spawnUri()
  • Dart – Isolatestatic Future<Isolate> spawn()
  • Flutter – used directlycompute()

spawnUri

SpawnUri has three required parameters:

  • The first is theUri, specify a newIsolatePath to the code file
  • The second is a parameter list of typeList<String>
  • The third is a dynamic message of typedynamic

Note that the code file used to run the new Isolate must contain a main function, which is the entry method for the new Isolate. The args parameter list in the main function corresponds to the second parameter in the spawnUri. If you do not need to send parameters to the Isolate, you can send an empty List to the Isolate.

It is used less often and cannot be used in Computer campaigns using DATr: UI. Check out this article on THE Isolate for asynchronous programming in the Dart language.

spawn

Spawn takes two required parameters

  • Functions to run (time-consuming tasks)
  • Dynamic messages, usually used for deliverymain IsolateSendPortobject

A quick demo:

void main() {
 var totalCount = await createTask();
 print($totalCount);
}
​
Future createTask() async {
 final p = ReceivePort();
 await Isolate.spawn(calculateInBackground, p.sendPort);
 return await p.first;
}
​
Future doBusyWorkInBackground(SendPort p) {
 final totalCount = await calculateCount(5000000000);
 // Use exit instead of send to submit efficiency
 return Isolate.exit(p, totalCount);
}
​
// Define a time-consuming task
Future<int> calculateCount(int targetCount) async {
 var totalCount = 0;
 for (var i = 0; i < targetCount; i++) {
   totalCount += i + i + 1;
}
 return totalCount;
}
Copy the code

This is basically the code in the createTask and doBusyWorkInBackground methods. Here you can see that there are two concepts, a ReceivePort and a SendPort, and SendPort uses SendPort from ReceivePort. SendPort and ReceivePort are actually channels for communication between two Isolate, but note that this channel is one-way, which means, One pair of SendPort and ReceivePort can transmit messages only from A Isolate to B Isolate. If B needs to transmit messages to A Isolate, another pair of SendPort and ReceivePort is required.

Another point to note here is isolate. exit, which is a syntax introduced after Dart 2.15. Previously, send was used to transmit messages. Then it will also lead to interface lag. The isolate.exit does not make a deep copy of the data, but passes it directly. This implementation also relies on the concept of Isolate groups introduced after Dart 2.15.

The Isolate in the Isolate group shares various internal data structures that represent running programs. Although the Isolate group still prevents sharing access to mutable objects between isolates, the Isolate group uses the shared heap implementation, which makes it possible to pass objects directly. In addition, the Isolate group also makes individual isolates in groups more portable. Because there was no need to initialize the program structure, starting an additional Isolate in an existing Isolate was over 100 times faster than before, and the resulting Isolate consumed 10 to 100 times less memory.

compute

Can see the Dart create a Isolate somewhat tedious, Flutter official further package provides a simpler API, it is to compute, located in the package: Flutter/foundation. The Dart. See how to rewrite the above program using compute:

void main() {
 var totalCount = await createTaskByCompute();
 print($totalCount);
}
​
Future createTaskByCompute() async {
 return await compute(calculateCount, 5000000000);
}
Copy the code

It takes two required arguments:

  • The method to execute
  • Dynamic message type, which can be arguments to the function being run

Process continuous stream data

The Demo above deals with single data, and the Isolate can also deal with continuous data. Check out Dart 2.15 and see what you can do with the Isolate. There is a small Demo for streaming data.

Usage scenarios

The Isolate should not be abused. Dart event loops should be used as much as possible to process asynchronous tasks so as to make full use of the Dart language. One of the easiest ways to determine when to use Future and Isolate is based on task time:

  • If it takes a few milliseconds or tens of milliseconds, useFuture
  • Others that take more time should be usedIsolateTo implement the

Some reference scenarios:

  • JSON decode
  • encryption
  • Image processing: for example, cropping
  • Network request: load resources, images

Write after sunset

Since Flutter 2.8.0, the concept of Isolate groups has been introduced and exit is used to pass parameters, which is much better than before.

Reference article:

Dart 2.15 update

Dart language asynchronous programming Isolate

Dry goods | Dart, a concurrent mechanism

Dart 2.15 update the ISOLATE should be used this way

Here’s a video:

Isolates and multithreading in Flutter

OK, that’s the end of this article. How do you praise, Chinese people do not deceive Chinese people you are really handsome.