Code-Garage #113 - Le concept de "race condition" et de "mutex" vulgarisés !

Durée: 11m26s

Date de sortie: 10/12/2024

En programmation dès lors que l'on touche aux multithreading, ou aux opérations asynchrones en générales, le risque de rencontrer des "race conditions" peut devenir critique !

Salut et bienvenue dans ce nouvel épisode du podcast de Code Garage.
Je m'appelle Nicolas Brunin-Bernard et aujourd'hui, on va aborder deux sujets qui sont évidemment
liés.
Le premier sujet, c'est les race conditions et le deuxième sujet, c'est les mutex.
Alors d'abord, qu'est-ce qu'une race condition ? En gros, c'est un problème en programmation
qu'on rencontre quand on a plusieurs threads ou plusieurs processus qui vont accéder en
même temps à la même ressource partagée.
Alors quand on parle de ressource partagée, ça peut être un petit peu tout.
Ça peut être une variable, ça peut être un fichier, ça peut être plein de choses.
Et donc, quand on dit qu'elle est accédée par plusieurs choses en même temps, si c'est
au sein d'un programme, eh bien ça peut être plusieurs threads qui accèdent à la
même variable.
Donc, ils vont lire ou écrire à la même variable et à peu près en même temps.
Mais si c'est dans le cas d'un processus par exemple, eh bien c'est deux processus
ou plus qui vont accéder au même fichier.
Si jamais on n'a pas de mécanisme de synchronisation, si on fait simplement un programme de base
et on va prendre un exemple très, très simple, admettons qu'on a une variable qui
s'appelle count qui est à zéro au début, c'est un entier.
Et on va avoir dans notre programme, eh bien deux threads qui vont mettre à jour ce compteur.
Donc, les deux threads sont indépendants, on les lance plus ou moins en même temps.
En tout cas, on leur demande de s'exécuter l'un à la suite de l'autre, mais ils vont
tourner en même temps.
Et on va chacun leur demander d'incrémenter le compteur de 1.
Là, évidemment, on va avoir un problème qu'on comprend très vite, c'est que, alors
là, selon le langage de programmation et l'environnement, il peut se passer plusieurs
choses, mais donc si les deux accèdent à la même valeur en même temps et essayent de
la modifier, eh bien ça veut dire qu'il y en a un qui va potentiellement lire la valeur
de la variable et la mettre à jour, mais entre temps, l'autre programme a également
lu la valeur de la variable et va essayer de la mettre à jour.
Et donc, il y en a un des deux qui va simplement écraser la modification qu'avait fait l'autre.
Et donc, on va avoir un comportement où, admettons qu'on leur demande chacun de tourner
dix fois, eh bien au lieu de se retrouver avec un compteur à la fin qui arrive à 20,
eh bien on peut se retrouver avec un compteur qui arrive à 15, 16, 18, 19, tout simplement
parce qu'il y a quelques étapes dans chacun des threads et chaque thread avait une boucle.
Eh bien sur certaines étapes, les deux threads se sont mis justement en compétition dans
la même course et puis ils ont modifié la même variable et ces modifications-là
sont venues s'écraser de temps en temps.
C'est ça qui est très compliqué avec justement des Ray conditions, c'est que parfois, très
souvent, eh bien lors le résultat ou le problème que ça va engendrer, eh bien il est très difficile
à reproduire puisque on ne va pas forcément se retrouver dans les mêmes conditions pour
différentes exécutions de ma même programme ou si c'est pour plusieurs processus ou peu
importe.
Donc, voilà très grosso modo ce qu'est une Ray condition.
Ici, à retenir le terme « race », il est utilisé pour l'anglais « course ». On peut
parler de compétition, voilà, mais c'est une course à l'accès à une donnée où
on parle en général de ressources partagées.
Donc, voilà ce qui peut se passer.
Donc, parfois, si c'est un accès aux fichiers, c'est même pas simplement la valeur du fichier
qui va ne pas être bonne, mais c'est simplement qu'il y a un des processus qui ne va pas
du tout pouvoir accéder aux fichiers puisque l'autre est déjà en train de le modifier
ou de le lire et donc ça va créer un processus qui va avoir une exception, qui va s'arrêter
et qui va mal fonctionner.
Donc, c'est vraiment très problématique.
Évidemment, on a des choses, on a des mécanismes pour éviter ça et ces mécanismes, on appelle
ça des mutex.
Un mutex, c'est la contraction des mots « mutual exclusion ».
Ok.
Et en fait, c'est un outil de synchronisation qui va garantir qu'une seule tâche, donc
là, comme on disait, ça peut être un thread, un processus, peu importe, ne va pouvoir
accéder à une ressource partagée que lorsque cette ressource est disponible.
Il y a une seule thread, par exemple, qui va accéder à la ressource en même temps.
En gros, ça agit comme un verrou.

Donc, le thread, par exemple, il doit acquérir le mutex avant d'accéder à la ressource
et une fois qu'il y a accédé, qu'il a fait les modifications dont il avait besoin,
et bien, il va libérer le mutex quand son travail va être terminé.
Si on prend un exemple de la vraie vie, ça revient littéralement à un verrou.
Imaginons des toilettes publiques, par exemple.
Eh bien, vous ne pouvez rentrer dans les toilettes que si le verrou est ouvert.
Et en général, normalement, ça veut dire si le verrou est ouvert, qu'il n'y a personne
dans les toilettes.
Ok.
Et tant que vous avez le verrou fermé, même si vous avez 1, 2, 3, 10, 20, 100 personnes
qui veulent accéder aux toilettes, eh bien, elles vont devoir se mettre les uns à la
suite des autres et attendre que le verrou se libère pour qu'il y ait une nouvelle
personne qui rentre, le verrou va se refermer, etc., etc., etc.
Voilà comment est-ce qu'on peut représenter un mutex dans la vraie vie.
C'est littéralement un verrou.
Là, je vous ai pris l'exemple qui n'est pas le plus glorieux puisque on parle de toilette,
mais j'espère que ça vous aura aidé à comprendre.
Globalement, si on refait notre exemple de tout à l'heure avec notre The Thread qui
essaye d'incrémenter un compteur qui ont toujours 10 boucles chacun, eh bien là, on
sait forcément qu'à la fin de l'exécution de notre The Thread, eh bien, la valeur de
notre compteur sera bien à 20 puisque à chaque fois qu'une boucle d'un des threads
va vouloir accéder à la variable compteur, la lire et la mettre à jour, eh bien, il
va falloir qu'elle demande de récupérer l'accès au mutex, ok ? On dit qu'elle
va l'acquérir.
Elle va modifier la ressource, ça va faire tout ce nom qu'elle a besoin.
Et puis à la fin, elle va libérer le mutex et ce n'est qu'une seule fois qu'elle aura
libéré le mutex, que l'autre thread va pouvoir faire sa mise à jour à son tour.
Alors, il faut savoir que, grosso modo, il y a deux approches qui existent sur les mutex
en programmation.
Il y a des mutex qui vont inclure la ressource au sein même du mutex, ok ? Et il y a d'autres
langages de programmation qui vont utiliser un mutex séparé de la ressource.
Le premier, par exemple, quand on a un mutex qui inclure la ressource, c'est ce que fait
Rust.
Donc, Rust, quand on va déclarer un mutex à l'intérieur du mutex, on va y mettre une
valeur plutôt.
Et donc, quand on va accéder au mutex, on va demander le verrouillage du mutex.
Pour nous, on va pouvoir venir modifier la valeur qui est à l'intérieur et puis ensuite,
on pourra évidemment faire le release de ce mutex.
Mais on a d'autres langages comme le piton.
Le piton, on appelle ça des locks, mais c'est le même principe.
Mais simplement, on va venir avoir une variable compteur, count, par exemple, et une variable
dans laquelle on va stocker un mutex, un lock.
Et là, il faudra bien penser à vérifier toujours ce même système, mais du coup, qu'on
peut effectivement modifier notre variable avec le bon lock.
Mais là, les deux approches, en fait, il y en a une qui est plus dangereuse que l'autre
puisque en piton, si jamais vous vous trompez et que vous vérifiez le mauvais lock et que
ce n'est pas ce lock qui correspond à cette variable-là, vous pouvez quand même vous
retrouver avec des raise conditions.
Maintenant, le côté en Rust où la variable est à l'intérieur d'un lock, enfin d'un
ça pose une autre différence, c'est que même si la variable dans le reste de votre
code est utilisée de manière synchrone, donc pas par différents threads, mais simplement
dans du code synchrone, il faudra quand même que ce code ouvre le mutex, le referme, etc.
Même s'il n'y en a plus vraiment besoin de ce mutex à ce moment-là, on aura un petit
peu moins de flexibilité.
Néanmoins, ça permet quand même de ne pas pouvoir de faire de faute d'inattention, enfin
voilà, ça demande, je dirais, un petit peu moins de discipline puisque la discipline
est enforcée par le mécanisme du langage.
Alors, on arrive à la fin de cet épisode.
Je vais quand même rajouter deux petits détails parce que je pense que c'est important.
La première, c'est qu'en général, effectivement, on va parler de raise condition plutôt pour
le multi-trading, comme j'en ai parlé, pour des processus qui accèdent à des valeurs
partagées comme des fichiers, des choses comme ça, mais en réalité, parfois, on va
parler de raise conditions pour des choses bien plus globales.
Ça peut être, par exemple, quand deux procédures stockées en base de données accèdent à la
même table au même moment, enfin la même ligne de la même table au même moment, ça
peut être bien généralisé dans la programmation en général et parfois, on va utiliser ce
terme de raise conditions même en dehors de cette scope de multi-tradings ou de processus.
Ça, c'était la première petite chose.
La deuxième chose, c'est que j'ai fait simple et j'ai parlé des raise conditions et des
mutex parce que je pensais que les deux sujets étaient forcément liés et qu'ils allaient
assez bien ensemble, mais évidemment, il y a d'autres méthodes pour éviter les raise
conditions.
Je vous ferai peut-être un épisode là-dessus pour aller un petit peu plus en profondeur.
Si jamais ça vous tente, n'hésitez pas à me le dire dans les commentaires.
Mais voilà, effectivement, j'ai présenté ça un petit peu comme la seule solution.
Ce n'est pas la seule solution, mais c'était pour faire au plus simple pour que les gens
qui ne connaissaient pas du tout ces concepts-là puissent les comprendre le mieux possible.
J'espère que cet épisode vous aura plu.
Moi, je vous donne rendez-vous la semaine prochaine pour un prochain épisode.
En attendant, pensez à mettre un avis si ce n'est pas déjà fait sur Spotify, Apple Podcast,
toutes les plateformes, évidemment, de podcast.
Et venez faire un tour sur code-garage.fr.
On a évidemment tous nos épisodes de podcast, tous nos articles de blog, mais surtout,
tous nos cours complets pour apprendre, récupérer des certificats de validation de la compétence
et surtout, vous pouvez mettre en avant votre profil, créer vos projets, vous faire un
portfolio.
On a essayé de mettre un maximum de fonctionnalités pour pouvoir faciliter la vie des devs et un
petit peu suivre tout le parcours d'apprentissage et de mise en avant de son profil.
Donc vraiment, si vous êtes dans une phase où vous cherchez à monter en compétence,
où vous cherchez à trouver un premier job ou un nouveau job ou même simplement si vous
cherchez d'émissions freelance, on a tout ce qu'il faut pour vous aider.
A la semaine prochaine, prenez soin de vous.
Salut !

Les infos glanées

Je suis une fonctionnalité encore en dévelopement

Signaler une erreur

Code-Garage

Découvrons ensemble des sujets passionnants autour du métier de dev et de la programmation en général !
Tags
Card title

Lien du podcast

[{'term': 'Technology', 'label': None, 'scheme': 'http://www.itunes.com/'}]

Go somewhere