Connaissez-vous Lwt? C'est une bibliothèque proposant un modèle asynchrone autour des fibres, ou threads légers (déjà qu'un thread était un processus léger, est-ce qu'une fibre est un processus vachement léger?) coopératifs, c'est à dire que la fibre va s'exécuter jusqu'à ce qu'elle laisse le champ libre à une autre fibre, via des appels spéciaux, par exemple pour effectuer des opérations d'entrée sortie, ou attendre un résultat fourni par une autre fibre.
Je trouve le manuel un poil léger, donc voici une série d'exemples très simples pour se faire une idée de l'utilisation du bazar. L'idée est généralement de créer un thread lwt, puis de le faire tourner via un appel à Lwt_main.run jusqu'à ce que l'exécution du thread soit complétée, et d'en récupérer une valeur de retour.
Dans un terminal ocaml, ces commandes permettent de charger les bibliothèques lwt:
#use "topfind";;
#require "lwt";;
#require "lwt.extra";;
C'est parti. L'exemple le plus simple: ce bout de code créé un bête thread lwt déjà terminé, contenant la valeur 3. Le Lwt_main.run attend que le thread soit complété et en retourne la valeur. Dans ce cas, il n'a rien à faire que de simplement retourner 3.
let t = Lwt.return 3 in
Lwt_main.run t
Un premier exemple faisant enfin quelque chose: l'on utilise la fonction Lwt_unix.sleep, lequel retourne un thread qui se termine au bout d'un certain temps. Cette fonction va donc dormir pendant 2 secondes avant de retourner unit.
let t = Lwt_unix.sleep 2. in
Lwt_main.run t
Voici l'utilisation d'une routine d'entrée sortie. Parce que les threads sont coopératifs, il est nécessaire de ne jamais bloquer ou utiliser des fonctions d'entrée / sortie non bénies par lwt. L'on utilisera donc Lwt_io pour lire et écrire des fichiers ou les entrées sorties standard. Le modèle est le même: notre thread écrit "Miaou" sur la sortie standard, et le thread est complété.
let t = Lwt_io.write_line
Lwt_io.stdout
"Miaou"
in
Lwt_main.run t
Ah, la fonction Lwt.bind! Elle permet de chaîner les threads: quand un thread est complété, il passe son résultat au suivant, qui s'exécute à son tour. Dans notre cas, le plus simple, nous passons unit à la fonction qui écrit. Vous l'aurez deviné, ce bout de code attend 2 secondes, puis affiche "Miaou" à l'écran.
let t =
Lwt.bind
(Lwt_unix.sleep 2.)
(fun () -> Lwt_io.write_line Lwt_io.stdout "Miaou")
in
Lwt_main.run t
Enfin, un exemple démontrant les capacités asynchrones de Lwt. Dans cet exemple, l'on créé 3 threads qui chacun attendent un certain temps avant d'afficher un message. La fonction Lwt.join retourne une fois que les 3 threads sont complétés. Quelque soit l'ordre dans lequel les threads ont été passés à la fonction, ils s'afficheront bien en temps voulu.
let sleep_and_print duration message =
Lwt.bind
(Lwt_unix.sleep duration)
(fun () -> Lwt_io.write_line Lwt_io.stdout message)
in
let t = Lwt.join
[sleep_and_print 3. "3 secondes";
sleep_and_print 4. "4 secondes";
sleep_and_print 2. "2 secondes"]
in
Lwt_main.run t
Voilà les bases, à partir de là il ne s'agit plus que de chaîner les fonctions d'entrées sorties Lwt décrites dans le manuel.
J'ai l'intention d'essayer d'écrire un petit moteur asynchrone orienté événements, permettant à l'utilisateur de souscrire dynamiquement à un certain nombre d'entrées, comme par exemple une horloge, des entrées réseau, ou des passages de messages (probablement pas pour OpenRailz, mais on ne sait jamais...).
Le mot de la fin: OCaml ne permet pas l'exécution concurrente de code car il possède, tout comme Python d'ailleurs, un verrou global. Lwt ne change pas cette règle, et n'est donc pas orienté vers du "number crunching", par exemple, mais permet en revanche de designer de manière très élégante des applications réactives devant gérer des événements complexes. Les appels coopératifs s'assurent que l'application n'est jamais en train d'attendre alors qu'un autre bout de code pourrait être exécuté, ce qui permet d'aller taper dans la base de données, écouter des messages réseau, lire des fichiers, et logger dans d'autres d'une manière transparente et claire. Cool non?
Aucun commentaire:
Enregistrer un commentaire