On note que la question immédiate est de savoir si la raison de la lenteur est la vitesse de calcul ou les entrées sorties (dans le premier cas, on est plus rapidement limité en accélération).
equilibre usage CPU et reseau
En revanche, si le traitement est limité par les entrées/sorties, la concurrence peut améliorer l’efficacité. Pendant qu’une partie du système attend que les entrées/sorties soient disponibles, une autre partie peut en profiter pour effectuer une autre opération et ainsi employer plus efficacement le processeur. ???
Le premier choix est de lancer un thread par client, le choix est discutable parce que cela peut lancer beaucoup de threads, mais surtout parce que l'on a pas de contrôle sur le scheduling et parce que la classe a un gros paquet de responsabilité.
On montre ensuite comment séparer la partie scheduling dans une classe dédiée à laquelle on poste les demandes de connexions, il est ensuite facile de changer le scheduler.
Ajouter des threads Observations concernant le serveur
- gestion de la connexion par socket ;
- traitement du client ;
- stratégie de gestion des threads ;
- stratégie d’arrêt du serveur.
le code traverse allègrement plusieurs niveaux d’abstraction différents > ???? la gestion des threads doit se trouver en quelques endroits parfaitement maîtrisés > ???
A faire a priori ???
Le bytecode généré par le programme en question est surprenant. Il y a beaucoup d'instructions, et certaines paraissent moyennement raisonnables. On a ensuite la présentation du calcul du nombre de chemin qui revient simplement à déterminer le nombre de permutations.
n = (NT)! / N!^T
Ce problème est simplement dû au fait que les lectures et écritures ne sont pas atomiques et donc on peut avoir deux threads qui lisent la même valeur et font leur écriture avec la même valeur à nouveau (car même incrément).
[NOTE]: En réalité la situation peut être bien pire, le langage Java, comme les autres langages un peu raisonnables, autorise les comportements faibles.
[PAS DANS LE LIVRE]
Regarder de plus près. Basé sur une technique de polling, permet de s'affranchir des threads lorsque l'on fait des IOs. [FIN]
Présentation succincte du framework Executor qui permet de poster un certain nombre de tâches à réaliser dans un pool. Il décharge ensuite les tâches postées (notamment en permettant d'implémenter la notion de futures).
Présentation des opérations comme compare_and_swap ou fetch_and_add qui permettent de réaliser un certain nombre d'opérations atomiques. On peut se référer à compare and swap qui est basiquement : si la variable que je veux set à la valeur recherché, écrire atomiquement la valeur voulue, sinon renvoyer la valeur trouvée.
Lorsque l'on a besoin d'utiliser des éléments non-sûrs,
- on peut synchroniser côté client, [Note]: ce qui est généralement une bombe à retardement
- faire une classe wrapper
- utiliser une classe safe équivalente [pas dans le livre] - Possibilité de créer un objet synchronisé à partir d'un objet pas synchronisé.
Présentation d'un exemple où de multiples appels provoquent des interférences principalement dus au fait que l'on peut reposer sur une connaissance qui est rapidement invalide.
[Note]: C'est l'invariant de la classe qui ne tient pas trop la route. On ne peut pas reposer sur le fait que la connaissance est toujours valide.
- Tolérer la panne
Technique de sagouin.
- Verrouillage côté client
On présente l'opération a possibilité d'échec qui devrait en fait être encapsulée.
- Verrouillage côté serveur
On introduit la possibilité d'échouer pour l'opération next. Ici, c'est réalisé à travers un adapteur, qui peut être une bonne manière de transformer un objet non-safe en objet safe.
Présente simplement une manière d'estimer les gains en multi-thread, mais la technique n'est pas très réaliste. Dans l'idée : on calcule le chevauchement maximum que l'on peut avoir entre IO et calcul et on obtient un temps minimum.
Condition d'un interblocage :
- exclusion mutuelle : même ressources (en quantité limitée) pour différents threads qui ne peuvent pas être utilisées en même temps,
- détention et attente : on obtient une ressource et on en attend d'autre sans libérer,
- pas de préemption : on ne peut pas piquer une ressource à un thread,
- attente circulaire : T1 a pris R1 et veut R2, T2 a pris R2 et veut R1.
- ressource simultannées,
- augmenter le nombre de ressources,
- vérifier que tout est libre avant de prendre.
On peut refuser l'attente, mais on a alors risque de famine ou livelock.
Système de requête pour demander les ressources occupées. On a quasiment les mêmes risques pour avant.
On évite l'interblocage par la mise en place de stratégie qui cassent les cycles. Par exemple en prenant toujours les ressources dans le même ordre.
BOF, beaucoup d'aberrations.
Framework de test permettant de simuler le scheduling.
https://deadlockempire.github.io/
What about Pony ?