Parlons Multithreading en Flutter

Qui suis-je ?
Thomas Ecalle
-
Développeur mobile Android natif depuis 3 ans
-
Développeur Flutter passionné depuis 1an
-
Mail : thomasecalle@gmail.com
-
Github : https://github.com/ThomasEcalle
-
Linkedin: https://www.linkedin.com/in/thomas-ecalle-239568120/
-
Medium: https://medium.com/@thomas_49096
Cyllene - Multithreading en Flutter
Sommaire
-
Asynchronisme vs Parallélisme vs Concurrence
-
Comment le langage Dart fonctionne ?
-
Penchons-nous sur les Futures
-
Le vrai Multithreading : les Isolate
Cyllene - Multithreading en Flutter
Asynchronisme
vs Parallélisme
vs Concurrence
Cyllene - Multithreading en Flutter
Asynchronisme
- Toutes les activités I/O (Input / Output)
- appels réseaux
- écriture sur le disque
- ...
- Votre programme n'est pas responsable du calcul
- Exemple d'appels réseaux multiple
Définition Wikipédia :
asynchrony, in computer programming, refers to the occurence of events independent of the main program flow and ways to deal with such events.
Cyllene - Multithreading en Flutter
Parallélisme
Parallel computing is a type of computation in which many calculations or the execution of processes are carried out simultaneously. Large problems can often be divided into smaller ones, which can then be solved at the same time.
- Utiliser les processeurs pour répartir le travail
- Chaque processeur peut effectuer une partie du travail
- A la fin, on regroupe l'ensemble des résultats
- Exemple de traitement d'images
Définition Wikipédia :
Cyllene - Multithreading en Flutter
Concurrence
Concurrent computing is a form of computing in which several computations are executing during overlapping time periods - concurrently - instead of sequentially (one completing before the next starts).
- Plusieurs services en parallèlle
- Chaque service a une responsabilité propre
- Ils peuvent (ou non) communiquer les uns avec les autres
- Exemple de service de BDD, service de logs, etc.
Définition Wikipédia :
Cyllene - Multithreading en Flutter
Finalement
C'est une question de point de vue
- Paralléliser l'algo ? Parallélisme
- Paralléliser les accès à l'I/O ? Asynchronisme
- Paralléliser les acteurs ? Concurrence
Cyllene - Multithreading en Flutter
Comment le langage Dart fonctionne ?
Cyllene - Multithreading en Flutter
Un langage Single Thread
-
Un seul Thread (Isolate) créé au démarrage de l’application
-
Dart n’exécute qu’une instruction à la fois
BLOQUANT
void myBigLoop(){
for (int i = 0; i < 1000000; i++){
_doSomethingSynchronously();
}
}Cyllene - Multithreading en Flutter
L'Event Loop
- Les évènement asynchrones sont référencés dans la pile des évènements : Event Queue
- I/0
- Gestures
- Streams
- ...
- Futures
- Lorsqu'elle en a la possibilité, l'Event Loop pioche dans la pile le prochain à traiter
Cyllene - Multithreading en Flutter
L'Event Loop
L'Event Loop est une boucle infinie qui check régulièrement si une opération I/O doit être exécutée ou est terminée
Cyllene - Multithreading en Flutter

Séquençage du code
- Exécution des opérations synchrones
- Dès que possible, l'Event Loop pioche dans la Queue un évènement asynchrone à gérer
Ce n'est donc pas de la vraie concurrence
Cyllene - Multithreading en Flutter
Penchons-nous sur les Futures
Cyllene - Multithreading en Flutter
Définition
- Tâche s'executant de manière asynchrone et se terminant (avec succès ou non) dans le futur
- Equivalent des Promises en Javascript
Cyllene - Multithreading en Flutter
void main() {
Future(() {
return 42;
}).then((value) {
print("Value: $value");
}).catchError((error) {
print("Error: $error");
});
}Cyllene - Multithreading en Flutter
void main() {
_getIntFromNetwork().then((int value) {
print("Got value: $value");
}).catchError((error) {
print("Got an error: $error");
});
}
Future<int> _getIntFromNetwork() async {
await Future.delayed(Duration(seconds: 2));
return 42;
}Cyllene - Multithreading en Flutter
Comment cela fonctionne en interne ?
Cyllene - Multithreading en Flutter
-
La Future est créée et répertoriée quelque part
-
Le code de la Future est mis dans la Queue des Event
-
L'instance de la Future est retournée avec un statut incomplet
-
Si il y en a, les instructions suivantes sont effectués
-
Lorsqu'elle le peut, l'Event Loop pioche le code de la future et l'execute
- Après quoi then ou catchError seront appelés
Cyllene - Multithreading en Flutter
Dans quel ordre les messages s'afficheront ?
void main(){
print('1');
Future((){
print('2');
}).then((_){
print('3');
});
print('4');
}Cyllene - Multithreading en Flutter
Résultat
1
4
2
3- Premier print
- Le code de la Future est ajouté à l'Event Loop
- Print 4
- L'Event loop pioche le code de la Future
- Le then est appelé
Cyllene - Multithreading en Flutter
Ce qu'il faut en retenir :
- Les Futures ne sont pas effectuées en parallèle !
- L'ensemble du code respecte toujours le même séquençage, les Futures ne font pas exception
Cyllene - Multithreading en Flutter
void main() {
print("1");
Future(() {
print("2");
}).then((value) {
print("3");
}).catchError((error) {
print("Error: $error");
});
for (int i = 0; i < 10000000; i++) {
print("ok $i");
}
}Ici, la boucle s'effectuera toujours avant les prints 2 et 3
Cyllene - Multithreading en Flutter
Le cas async / await
Cyllene - Multithreading en Flutter
- Une fonction avec le mot clé async retourne une Future
- Le code de la fonction est effectué de manière synchrone jusqu'au premier await
- La méthode attend ensuite que la Future soit terminée pour reprendre son exécution
Cyllene - Multithreading en Flutter
void main() {
print("1");
Future(() {
print("2");
}).then((_) {
print("3");
});
}void main() async {
print("1");
await Future(() {
print("2");
});
print("3");
}Ces deux codes sont strictement équivalents :
Cyllene - Multithreading en Flutter
async / await ne change RIEN au séquençage habituel et à l'Event Loop
Ce qu'il faut en retenir
Cyllene - Multithreading en Flutter
Quelques exemples
Qu'affichent ces différents programmes ?
Pourquoi ?
Cyllene - Multithreading en Flutter
void main() async {
await a();
await b();
}
Future<void> a() async {
await Future.delayed(Duration(seconds: 1));
print("A");
}
Future<void> b() async {
print("B start");
await c();
print("B end");
}
Future<void> c() async {
print("C start");
Future(() => print("C Middle")).then((_) => print("C Middle End"));
print("C end");
}Cyllene - Multithreading en Flutter
void main() {
final List<String> list = ["Flutter", "is", "awesome"];
a(list);
b(list);
}
void a(List<String> list) async {
print("Before loop A");
list.forEach((String value) async {
await delayedPrint(value);
});
print("After loop A");
}
void b(List<String> list) async {
print("Before loop B");
for (int index = 0; index < list.length; index++) {
await delayedPrint(list[index]);
}
print("After loop B");
}
Future<void> delayedPrint(String value) async {
await Future.delayed(Duration(seconds: 1));
print(value);
}Cyllene - Multithreading en Flutter
| a() | b() |
|---|---|
| Before loop A | Before loop B |
| After loop | Flutter (après 1 seconde) |
| Flutter (après 1 seconde) | is (après 1 seconde) |
| is (tout de suite après) | awesome (après une seconde) |
| awesome (tout de suite après) | After loop B (tout de suite après) |
Cyllene - Multithreading en Flutter
Pourquoi ce résultat différent avec a()?
-
forEach() prend une callback en paramètre
-
Celle-ci est notée async et est donc une Future
-
La callback est exécutée jusqu'au await puis tout le reste est mis dans la Queue des Events
-
L'execution suit son cours et "After loop A" est appelé
- Enfin l'Event Loop dépile toutes les callbacks d'un coup
Cyllene - Multithreading en Flutter
Petit bonus : Comment utiliser forEach quand même ?
void a(List<String> list) async {
print("Before loop A");
await Future.forEach(list, (String el) async {
await delayedPrint(el);
});
print("After loop A");
}Cyllene - Multithreading en Flutter
Le vrai Multi-Threading : Les Isolates
Cyllene - Multithreading en Flutter
Les Isolates

- Un seul Isolate créé au lancement de l'application
- Il a sa propre zone mémoire
- Il a sa propre Event Loop
Cyllene - Multithreading en Flutter
Nous pouvons créer d'autres Isolates


Cyllene - Multithreading en Flutter


Chaque isolate aura :
- Sa propre mémoire
- Sa propre Event Loop
Cyllene - Multithreading en Flutter
Comparons avec les Threads habituels :
Avantages :
- Pas de gestion de mémoire partagée (locks, etc.)
- Du vrai parallelisme
Inconvénients :
- Le partage d'informations entre Isolates est un peu plus "compliqué"
Cyllene - Multithreading en Flutter
Comment les utiliser ?
Cyllene - Multithreading en Flutter
Prenons cette application :

Cyllene - Multithreading en Flutter
Les boutons vont tous les deux lancer ce "gros" calcul :
int bigTask(num n) {
int result = 0;
for (int i = 0; i < n; i++) {
result += i;
}
return result;
}Cyllene - Multithreading en Flutter
Tout d'abord, en utilisant une Future :
_runOnMainThread(BuildContext context) {
Future<int>(() {
return bigTask(NB_ITERATIONS);
}).then((int value) {
_showSnackBar(context, value);
});
}Cyllene - Multithreading en Flutter
Résultat (pour 1 milliard d'itérations) :

Cyllene - Multithreading en Flutter
Utilisons un Isolate séparé !
Transformer notre code pour utiliser un Isolate va-t-il être un processus :
- Extrêmement long
- Extrêmement pénible
- Nécessitant une refacto de plusieurs jours et donc en fait on va garder un UI un peu crade parce qu'on a pas le temps pour ce sprint mais on se garde ça de côté pour le prochain, promis
L'UI freeze car la Future n'est pas réellement jouée en parallèle...
?
Cyllene - Multithreading en Flutter
NON
Cyllene - Multithreading en Flutter
Nous allons utiliser la manière la plus simple :
La méthode compute provenant de flutter/foundation.dart
Voici la méthode utilisant un Isolate séparé :
_runOnSecondIsolate(BuildContext context) async {
final int result = await compute(bigTask, NB_ITERATIONS);
_showSnackBar(context, result);
}Cyllene - Multithreading en Flutter
Attention, petit détail :
- Les méthodes lancées via compute doivent être static ou en dehors d'une instance de classe
Donc :
static int bigTask(num n) {
int result = 0;
for (int i = 0; i < n; i++) {
result += i;
}
return result;
}Cyllene - Multithreading en Flutter
C'est tout !
Cyllene - Multithreading en Flutter

Cyllene - Multithreading en Flutter
Résultat (pour 1 milliard d'itérations) :
La méthode compute nous a permis de :
- Créer et lancer un Isolate automatiquement
- Faire tourner notre code dans cet Isolate
- Attendre le retour pour lancer notre snackbar
Finalement nous avons :
- moins de code à écrire
- une UI qui ne freeze pas
- du vrai parallélisme !
Cyllene - Multithreading en Flutter
Petite précision :
La méthode compute peut prendre "du temps" à créer et lancer l'Isolate
Il arrive donc que ce ne soit pas la solution optimale
Cyllene - Multithreading en Flutter
Alors comment choisir ?
Selon le temps que prendra à priori votre tâche :
- quelques millisecondes ?
Les Futures sont un bon choix
- plusieurs centaines de millisecondes ?
Partez pour un Isolate !
Cyllene - Multithreading en Flutter
Pour aller plus loin :
Nous avons utilisé la manière la plus haut niveau et simple pour créer et utiliser un Isolate
Il est bien sûr possible d'avoir plus de contrôle sur l'Isolate et s'échanger des messages entre Main Isolate et Isolates tiers
Cyllene - Multithreading en Flutter
Isolate isolate;
void start() async {
final ReceivePort receivePort = ReceivePort();
isolate = await Isolate.spawn(task, receivePort.sendPort);
receivePort.listen((data) {
print('RECEIVE: ' + data + ', ');
});
}
void task(SendPort sendPort) {
int counter = 0;
Timer.periodic(Duration(seconds: 1), (Timer t) {
counter++;
sendPort.send("Counter value = $counter");
});
}
void stop() {
if (isolate != null) {
isolate.kill(priority: Isolate.immediate);
isolate = null;
}
}
void main() async {
await start();
await stdin.first;
stop();
print("Stopped processus");
exit(0);
}Cyllene - Multithreading en Flutter
Conclusion
Cyllene - Multithreading en Flutter
- Comprendre le fonctionnement de l'Event Loop
- Comprendre le fonctionnement des Futures
- Savoir utiliser les Isolates lorsque nécessaire
Pour une bonne UI et donc une bonne application, il est primordial de :
Cyllene - Multithreading en Flutter
Remerciements et sources
- A Didier Boelens pour ses articles géniaux : https://www.didierboelens.com/fr/
- Explication sur les différences entre asynchronisme, concurrence et parallélisme : http://sametmax.com/la-difference-entre-la-programmation-asynchrone-parallele-et-concurrente/
- A Flutter Paris / Edouard pour cette présentation
Cyllene - Multithreading en Flutter
Merci
Cyllene - Multithreading en Flutter
Thomas Ecalle
thomasecalle@gmail.com
Linkedin : https://www.linkedin.com/in/thomas-ecalle-239568120/
Github : https://github.com/ThomasEcalle
Medium: https://medium.com/@thomas_49096
Avez-vous des questions ?
Cyllene - Multithreading en Flutter
A suivre :
-
Code promo Flutter Europe
-
Un dernier petit challenge ...
Cyllene - Multithreading en Flutter

InteractParis241
Un dernier petit challenge ?
Cyllene - Multithreading en Flutter
Répondre sur : https://meetup-paris-isolates.firebaseapp.com
Dans quel ordre les messages s'afficheront ?
void main() async {
print("1");
await a();
print("7");
}
Future<void> a() async {
print("2");
Future(() => print("3")).then((_) {
throw Exception();
print("4");
}).catchError((error) {
print("5");
});
print("6");
}Cyllene - Multithreading en Flutter
Parlons Multithreading en Flutter
By Ecalle Thomas
Parlons Multithreading en Flutter
- 336