Dans un billet précédent, j'avais évoqué mon manque d'assurance en matière d'architecture concurrente. Les mutex et autres sémaphores ne me font pas peur, mais je ne retrouve pas en matière de concurrence l'aisance que j'ai lorsque je fais de la conception statique.
Or il se trouve que l'Internet reflète bien mon malaise. Lorsqu'on cherche des textes sur le sujet, on trouve d'innombrables introductions à la programmation concurrente, avec leurs lot habituel de primitives à utiliser pour définir des sections critiques ou pour résoudre des problèmes d'accès en lecture-écriture à une ressource. Mais lorsqu'on veut monter d'un cran et parler architecture, c'est une autre paire de manches. Pour trouver des design patterns pour la conception concurrente, il faut se lever tôt.
Pour faire un parallèle avec l'architecture statique, c'est comme si les design patterns avaient disparu du Net, et que tout ce qu'on trouvait était des articles sur comment écrire des conteneurs efficaces, des pointeurs intelligents et des techniques de sérialisation. C'est bien pour les exercices à la fac, mais pour écrire le prochain OS grand public, c'est un peu maigre.
J'ai quand même fini par tomber sur Multithreading, de Bo I. Sandén. Là pour le coup, même si on n'échappe pas à l'introduction aux primitives de synchronisation en Ada et en Java, c'est vraiment un ouvrage sur l'architecture concurrente. L'auteur introduit une méthode de conception qu'il appelle Entity-Life Modeling, ou modélisation de la vie d'entités. Les entités en question sont les threads et les objets protégés (safe objects).
ELM bases multithreading on a simple, intuitive principle in the manner of object orientation where you model software classes and their relationships on domain objects and their relationships. ELM puts thread structuring on par with class structuring.
Si l'ouvrage introduit des design patterns pour la conception dynamique, on ne peut pas vraiment parler de catalogue: M. Sandén en présente deux. On peut être un peu déçu du faible nombre, mais il y a une astuce : ces deux solutions sont duales, c'est-à-dire que les deux peuvent être utilisées pour résoudre un même problème. Cette idée est si puissante qu'elle justifie à elle seule la lecture du livre. Si vous avez une solution, vous pourrez toujours examiner sa complémentaire pour savoir si celle-ci n'est pas plus efficace. Génial!
Je ne vais pas vous les présenter en détail, le livre est là pour cela, mais voici tout de même le principe de base:
- Un thread utilisateur de ressources est un thread qui effectue l'ensemble d'un travail en acquérant successivement l'accès exclusif à des ressources partagées. Le thread représente donc une sorte de procédure complète. L'élément actif est le travail, alors que la ressource reste passive.
- Un thread protecteur de ressource est un thread qui possède l'accès exclusif à une ressource, et qui reçoit un travail ponctuel à effectuer sur cette ressource par l'intermédiaire d'une file d'attente. Ici, le travail global n'est pas décrit par un seul thread, mais par la façon dont les threads sont connectés entre eux. Ce sont les ressources qui sont actives, et le travail est "transporté" passivement de l'une à l'autre.
Ce qui est plus gênant, c'est le trajet pour atteindre la fin du livre. En effet, sous prétexte de couvrir plusieurs problématiques différentes, l'auteur revient coup sur coup sur un certain nombre d'exemples qui arrivent un peu délavés à la dernière page. L'avantage, comme je l'ai dit, c'est qu'à la fin on a bien compris. L'inconvénient, c'est que la lecture n'est pas folichonne. Avec une écriture un peu moins verbeuse (note: je ne suis peut-être pas le plus apte à le critiquer là-dessus...) et un style un peu moins académique, il me semble que le sujet aurait pu être traité de manière plus efficace en moitié moins de pages.
Face à cette lourdeur, que les citations un peu geek et quelques traits d'humour ne font pas oublier, c'est tout un ensemble de petites notions pratiques et intéressantes qui passent un peu inaperçues, comme l'optimalité d'une solution ou les activités concurrentes, et qui font vraiment la saveur de l'ouvrage. Ajoutons à cela que le titre ultra-général promet plus que ce que n'apporte le contenu: le livre s'intitule Multithreading mais décrit en réalité une méthode bien précise, quoi que portant elle-aussi un nom bien vague, Entity-Life Modeling, qui n'englobe que deux patterns bien concrets dans lesquels les mots entity et life n'apparaissent pas. Dommage!
A noter quand même au tableau des bonnes choses: le chapitre 3 est un cours assez efficace sur la modélisation de machines à états. Avec le chapitre 4 qui introduit les deux patterns évoqués ci-dessus, ils contiennent à eux deux la moitié de la valeur du livre. Si (comme moi) vous avez des scrupules à tuer des arbres mais n'aimez pas lire sur écran, vous pouvez vous contenter d'imprimer ces deux chapitres-là.