typographie concurrence.whata

This commit is contained in:
Raphaël Jakse 2016-02-28 00:18:36 +01:00
parent 68a77c80d2
commit 151e23dbbb
1 changed files with 24 additions and 24 deletions

View File

@ -6,18 +6,18 @@
La concurrence est à la fois similaire et différente du sujet que nous avons abordé dans le dernier chapitre, le parallélisme. Comme ces deux concepts impliquent tout les deux d'éxecuter un programme sur des [* threads], et que le parallélisme est basé sur la concurrence, ils sont souvent confondus.
Voici les différences entre le parallélisme et la concurrence :
Voici les différences entre le parallélisme et la concurrence~ :
- L'objectif principal du parallélisme est de profiter des multiples coeurs d'un processeur pour améliorer les performances d'un programme. La concurrence d'un autre côté, est un concept qui peut être utile même dans un environnement mono-coeur. La concurrence, c'est faire s'éxecuter un programme sur plus d'un [* thread] à la fois.
- En parallélisme, les tâches sont indépendantes les unes des autres, ce serait en fait un bug si une tâche dépendait d'autres tâches qui s'éxecutent au même moment. Dans le cas de la concurrence, il est tout à fait normal qu'un [* thread] dépende des résultats d'autres [* threads]
- Même si les deux modèles utilisent les [* threads] du système, les [* threads] du parallélisme sont encapsulées dans le concept de tâche. La concurrence utilise les [* threads] explicitement.
- Le parallélisme est facile à utiliser, et tant que les tâches sont indépendantes, il est facile de créer des programmes qui fonctionnent correctement. La concurrence est facile à mettre en place uniquement quand elle est basée sur les [* messages]. Il est très difficile d'écrire des programmes concurrents si ils sont basés sur le modèle traditionel de la concurrence, qui implique le partage de données verrouillées.
D permet d'utiliser les deux modèles de concurrence: les messages et le partage de données. Nous allons parler des messages dans ce chapitre et du partage de données dans le prochain chapitre.
D permet d'utiliser les deux modèles de concurrence~ : les messages et le partage de données. Nous allons parler des messages dans ce chapitre et du partage de données dans le prochain chapitre.
[ = Concepts
- [** [* Thread]] : Les systèmes d'exploitation considèrent les programmes comme des unités qu'ils appellent [* threads]. L'éxecution d'un programme D commence par la fonction [** main] dans le [* thread] qui a été assigné au programme par le système d'exploitation. Toutes les opérations du programme sont normalement executées dans ce [* thread]. Le programme est libre de démarrer d'autres [* threads] pour pouvoir exécuter plusieurs tâches simultanément. En fait, les tâches ont été vues dans le chapitre précédent, et sont basées sur des [* threads] démarés automatiquement par [c std.parallelism].
- [** [* Thread]]~ : Les systèmes d'exploitation considèrent les programmes comme des unités qu'ils appellent [* threads]. L'éxecution d'un programme D commence par la fonction [** main] dans le [* thread] qui a été assigné au programme par le système d'exploitation. Toutes les opérations du programme sont normalement executées dans ce [* thread]. Le programme est libre de démarrer d'autres [* threads] pour pouvoir exécuter plusieurs tâches simultanément. En fait, les tâches ont été vues dans le chapitre précédent, et sont basées sur des [* threads] démarés automatiquement par [c std.parallelism].
Le système d'exploitation peut mettre en pause l'exécution des [* threads] sans qu'on puisse prédire quand ni pour combien de temps. Même les opérations aussi simples que incrémenter une variable peuvent être interrompues pendant leur éxecution:
@ -25,13 +25,13 @@ D permet d'utiliser les deux modèles de concurrence: les messages et le partage
L'opération ci-avant implique trois étapes: Lire la valeur de la variable, l'incrémenter, et assigner la nouvelle valeur à la variable. Le [* thread] peut être mis en pause entre ces étapes et continuer sans qu'on sache quand.
- [** Message] : Les données qui sont transmises entre des [* threads] sont appelés des messages. Les mesages peuvent être composés d'une ou de plusieurs variables, de n'importe quel type.
- [** Message]~ : Les données qui sont transmises entre des [* threads] sont appelés des messages. Les mesages peuvent être composés d'une ou de plusieurs variables, de n'importe quel type.
- [** Identifiant de [* thread]] : Chaque [* thread] a un identifiant, qui est utilisé pour spécifiant le destinataire d'un message.
- [** Identifiant de [* thread]]~ : Chaque [* thread] a un identifiant, qui est utilisé pour spécifiant le destinataire d'un message.
- [** Père] : N'importe quel [* thread] qui démarre un autre [* thread] est le père de ce [* thread].
- [** Père]~ : N'importe quel [* thread] qui démarre un autre [* thread] est le père de ce [* thread].
- [** Fils] : N'importe quel [* thread] qui est démarré par un propriétaire est appellé un fils.
- [** Fils]~ : N'importe quel [* thread] qui est démarré par un propriétaire est appellé un fils.
]
@ -119,7 +119,7 @@ La sortie d'un de ces threads est précedée d'une flèche ([** >]):
La sortie du programme peut être différente entre deux éxecutions en fonction de comment le système d'exploitation a mis en pause et remis en route les [* threads].
Chaque système d'exploitation a une limite de [* threads] qui peuvent s'éxecuter simultanément. Cette limite peut être reglée par utilisateur, pour tout le système ou selon un autre critère. Les performances globales d'un système peuvent être alterées si il y a plus de [* threads] qui sont occupés que de coeurs dans le processeur. Quand un [* thread] est occupé à réaliser des opérations, on dit qu'il est [** attaché au processeur]. De nombreux [* threads] passent beaucoup de temps sans réaliser d'opérations, à attendre qu'un évenement se produise comme une entrée de l'utilisateur, l'arrivée de données sur une connection réseau, la fin d'un appel à [c Thread.sleep], etc. Ces [* threads] sont dits [** attachés à l'E/S] ([* E/S : Entrée/Sortie]). Quand ce genre de [* thread] s'éxecute, un programme peut se permettre de démarrer plus de [* threads] que de coeurs sans dégrader ses performances. Comme dans chaque décision qui concerne les performances d'un programme, il faut réaliser des mesures pour être certains des ressources que consomme effectivement chaque [* thread].
Chaque système d'exploitation a une limite de [* threads] qui peuvent s'éxecuter simultanément. Cette limite peut être reglée par utilisateur, pour tout le système ou selon un autre critère. Les performances globales d'un système peuvent être alterées si il y a plus de [* threads] qui sont occupés que de coeurs dans le processeur. Quand un [* thread] est occupé à réaliser des opérations, on dit qu'il est [** attaché au processeur]. De nombreux [* threads] passent beaucoup de temps sans réaliser d'opérations, à attendre qu'un évenement se produise comme une entrée de l'utilisateur, l'arrivée de données sur une connection réseau, la fin d'un appel à [c Thread.sleep], etc. Ces [* threads] sont dits [** attachés à l'E/S] ([* E/S~ : Entrée/Sortie]). Quand ce genre de [* thread] s'éxecute, un programme peut se permettre de démarrer plus de [* threads] que de coeurs sans dégrader ses performances. Comme dans chaque décision qui concerne les performances d'un programme, il faut réaliser des mesures pour être certains des ressources que consomme effectivement chaque [* thread].
]
@ -223,7 +223,7 @@ Les valeurs passées ensemble dans un message forment un tuple pour celui qui re
auto floating = message[2];
>>>]
Si les types ne correspondent pas, une exception [c MessageMismatch] est lancée:
Si les types ne correspondent pas, une exception [c MessageMismatch] est levée:
[code=d <<<
import std.concurrency;
@ -246,7 +246,7 @@ std.concurrency.MessageMismatch@std/concurrency.d(235):
Unexpected message type: expected 'double', got 'immutable(char)[]'
>>>]
Les exceptions que le fils peut lancer ne sont pas attrapées par le père. Une solution possible est de faire envoyer l'exception en tant que message par le fils. Nous verrons celà plus tard dans le chapitre.
Les exceptions que le fils peut lever ne sont pas attrapées par le père. Une solution possible est de faire envoyer l'exception en tant que message par le fils. Nous verrons celà plus tard dans le chapitre.
]
@ -621,7 +621,7 @@ recu: bonjour
[ = Exceptions pendant l'exécution du [* thread] fils
Comme nous l'avons vu dans le chapitre précedent, quand on utilise [c std.parallelism], une exception lancée dans une tâche peut être automatiquement attrapée par le [* thread] père. Cela permet de traiter ces exceptions:
Comme nous l'avons vu dans le chapitre précedent, quand on utilise [c std.parallelism], une exception levée dans une tâche peut être automatiquement attrapée par le [* thread] père. Cela permet de traiter ces exceptions:
[code=d <<<
try{
@ -644,7 +644,7 @@ void calculer(){
}
>>>]
La fonction [c to!double()] peut lancer une exception si la chaîne de caractère n'est pas convertible en valeur [c double]. Comme cette exception entrainerait la fin de l'éxecution de la fonction fille, le père dans le programme suivant ne pourrait recevoir qu'un seul message:
La fonction [c to!double()] peut lever une exception si la chaîne de caractère n'est pas convertible en valeur [c double]. Comme cette exception entrainerait la fin de l'éxecution de la fonction fille, le père dans le programme suivant ne pourrait recevoir qu'un seul message:
[code=d <<<
import std.stdio;
@ -739,7 +739,7 @@ resultat 1: ERREUR! 'no digits seen'
resultat 2: 3.9
>>>]
Un autre moyen de faire ça serait d'envoyer l'exception en elle-même au [* thread] père. La père peut alors utiliser l'exception ou la relancer:
Un autre moyen de faire ça serait d'envoyer l'exception en elle-même au [* thread] père. La père peut alors utiliser l'exception ou la lever à nouveau:
[code=d <<<
// ... Dans le fils ...
@ -765,7 +765,7 @@ receive(
Les [* threads] peuvent détecter la fin de l'exécution d'un receveur de message.
[ == L'exception [c OwnerTerminated]
Cette exception est lancée à la réception d'un message du père, si le père a terminé son exécution. Le [* thread] père suivant se contente d'envoyer deux messages à son fils et se termine. Cela lance une [c OwnerTerminated] dans le [* thread] fils:
Cette exception est levée à la réception d'un message du père, si le père a terminé son exécution. Le [* thread] père suivant se contente d'envoyer deux messages à son fils et se termine. Cela lance une [c OwnerTerminated] dans le [* thread] fils:
[code=d <<<
import std.stdio;
@ -783,14 +783,14 @@ void fonctionIntermediaire() {
void fonctionFils() {
while (true) {
auto m = receiveOnly!int(); // ← Une exception est lancée
auto m = receiveOnly!int(); // ← Une exception est levée
// si le père est terminé.
writeln("Message: ", m);
}
}
>>>]
La sortie :
La sortie~ :
[output <<<
Message: 1
@ -840,7 +840,7 @@ void main() {
while (true) {
auto m = receiveOnly!int(); // ← Une exception est
// lancée si le fils
// levée si le fils
// est terminé.
writeln("Message: ", m);
}
@ -910,7 +910,7 @@ Chaque [* thread] a une boîte aux lettres dédiée qui contient tout les messag
- [c OnCrowding.block]: L'expéditeur est mis en pause jusqu'à ce qu'il y ai de la place dans la boîte aux lettres.
- [c OnCrowding.ignore]: Le message est ignoré.
- [c OnCrowding.throwException]: une exception [c MailboxFull] est lancée à l'expéditeur du message.
- [c OnCrowding.throwException]: une exception [c MailboxFull] est levée à l'expéditeur du message.
- une fonction de type [bool function(Tid)]: la fonction spécifiée est appellée
Avant d'examiner un exemple de [c setMaxMailboxSize()], parlons de ce qui peut ammener une boîte aux lettres à grossir continuellement. Dans le programme suivant, le fils envoie des messages les uns à la suite des autres mais le père passe un peu de temps à recevoir chaque message:
@ -992,7 +992,7 @@ Certains messages peuvent être prioritaires par rapport aux messages ordinaires
[code=d <<< prioritySend(pereTid, ImportantMessage(100)); >>>]
Si le destinataire n'a pas de fonction qui gère le type du message prioritaire, une excepton [c PriorityMessageException] est lancée:
Si le destinataire n'a pas de fonction qui gère le type du message prioritaire, une excepton [c PriorityMessageException] est levée~ :
[output <<<
std.concurrency.PriorityMessageException@std/concurrency.d(280):
@ -1006,9 +1006,9 @@ Dans les programmes simples que nous avons vu avant, il était facile d'utiliser
Les trois fonctions suivantes sont une interface pour accéder à un tableau associatif au quel chaque [* thread] a accès:
- [c register()] : associe un [* thread] à un nom.
- [c locate()] : retourne le [* thread] associé à un nom. Si il n'y a pas de [* thread] associé au nom, alors [c Tid.init] est retourné.
- [c unregister()] : désassocie l'association entre un [* thread] et un nom.
- [c register()]~ : associe un [* thread] à un nom.
- [c locate()]~ : retourne le [* thread] associé à un nom. Si il n'y a pas de [* thread] associé au nom, alors [c Tid.init] est retourné.
- [c unregister()]~ : désassocie l'association entre un [* thread] et un nom.
La programme suivant démarre deux [* threads] qui communiquement entre eux par leurs noms. Ces [* threads] s'envoient des messages l'un à l'autre jusqu'à ce qu'ils recoivent un message [c Exit]:
@ -1107,8 +1107,8 @@ first, Je me termine.
- [c register()], [c unregister()] et [c locate()] permettent d'identifier les [* threads] par des noms.
- Des exceptions peuvent être lancées pendant le passage de messages : [c MessageMismatch], [c OwnerTerminated], [c LinkTerminated], [c MailboxFull] et [c PriorityMessageException].
- Des exceptions peuvent être levées pendant le passage de messages~ : [c MessageMismatch], [c OwnerTerminated], [c LinkTerminated], [c MailboxFull] et [c PriorityMessageException].
- Le père peut attraper automatiquement les exceptions lancées par un fils.
- Le père peut attraper automatiquement les exceptions levées par un fils.
]