Multithreading et synchronisation Flashcards

(64 cards)

1
Q

Thread (fil d’exécution)

A

une tâche indépendante à l’intérieur d’un processus

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
2
Q

Pourquoi les threads ?

A

Performance : profiter de plusieurs CPU
Génie logiciel : faciliter la programmation

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
3
Q

Les threads ont une vue mémoire commune

A

Pas d’isolation matérielle
• variables globales partagées
• tas d’allocation commun

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
4
Q

Pour les threads, l’ordonnancement est indépendant

A
  • un VCPU privé
    TCB = Thread Control Block

Une pile d’exécution privée
Des variables locales privées

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
5
Q

Qu’est que le TCB ?

A

Thread Control Block
= photo du processus, changer de contexte à l’intérieur d’un processus

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
6
Q

Que permettent les threads ?

A

Le multitâche à l’intérieur d’une même vue mémoire
( ≠ dupliquer plusieurs fois processus mais = cohabitation dans même espace virtuel )

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
7
Q

Qu’est-ce que le PCB ?

A

Structure de données qui décrit l’intégralité du processus en mémoire
= une PT + un/plusieurs TCB

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
8
Q

Un thread ne peut pas vivre en dehors d’un processus

A

besoin d’une vue mémoire

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
9
Q

Un processus vivant a toujours au moins un thread

A

main thread» = thread qui exécute main()
• lorsque zéro thread ▶ processus terminé

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
10
Q

L’ordonnanceur travaille avec des threads ou des processus ?

A

Des threads (unité d’ordonnancement)

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
11
Q

Problème à l’exécution

A

Code source ≠ instructions du processeur :
Ce qu’on lit n’est pas ce que la machine fait séquentiellement : différentes exécutions possibles

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
12
Q

Quels sont les risques liés aux accès concurrents à une ressource partagée ?

A

Corruption de données (le programme ne calcule pas ce que le programmeur voulait = bug) et/ou crash

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
13
Q

C’est quoi une data race (race condition, situation de concurrence) ?

A

Situation où le résultat du programme dépend de l’ordre dans lequel sont exécutées les instructions des threads

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
14
Q

Quels sont les différents types de race condition ?

A

Globalement, c’est quand on a plusieurs accès concurrents à une ressource partagée (variable globale, fichier, réseau, base de données…)

• deux écritures concurrentes = conflit
• une lecture et une écriture concurrentes = conflit

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
15
Q

Concurrence

A

parallélisme et/ou entrelacement
• i.e. quand on ne maîtrise pas l’ordre temporel des actions

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
16
Q

Précepte : data race = bug

A

Un programme dans lequel plusieurs tâches peuvent se retrouver en situation de concurrence est un programme incorrect.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
17
Q

Objectif lors de situations de concurrence

A

Garantir l’exclusion mutuelle
- On veut que chaque section critique s’exécute de façon atomique

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
18
Q

Action atomique

A

Action au cours de laquelle aucun état
intermédiaire n’est visible depuis l’extérieur

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
19
Q

Ressource critique

A

Objet partagé par plusieurs threads et
susceptible de subir une data race

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
20
Q

Section critique

A

Morceau de programme qui accède a
une ressource critique

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
21
Q

Exclusion mutuelle

A

Interdiction pour plusieurs threads de se trouver simultanément à l’intérieur d’une section critique

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
22
Q

Idée derrière l’exclusion mutuelle

A

« Verrouiller » l’accès à une section critique déjà occupée

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
23
Q

Quelles sont les deux primitives pour l’exclusion mutuelle par verrouillage ?

A

lock(L) et unlock(L)
cf slide 16

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
24
Q

lock(L)

A

pour prendre le verrou L en exclusivité
▶ un seul thread peut entrer en section critique

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
25
unlock(L)
pour relâcher le verrou L ▶ permet aux autres threads de le prendre à leur tour
26
Solution naïve pour mettre en place l’exclusion mutuelle : partagé int turn = 1
• Exclusion mutuelle : OK Attente active : exécution pas très efficace • Problème : alternance stricte ▶ progression non garantie
27
Quelles sont les propriétés souhaitables pour l’exclusion mutuelle ?
• Exclusion mutuelle : à chaque instant, au maximum une seule tâche est en section critique, sinon risque de data race • Progression : si aucune tâche n’est en section critique, alors une tâche exécutant lock() ne doit pas se faire bloquer, sinon risque de deadlock, en VF interblocage • Équité : aucune tâche ne doit être obligée d’attendre indéfiniment avant de pouvoir entrer en section critique, sinon risque de starvation • Généralité : pas d’hypothèses sur le nombre de tâches ou sur leurs vitesses relatives, on veut une solution universelle
28
Seconde solution naïve pour mettre en place l’exclusion mutuelle - booléen occup partagé
•Progression : OK •Exclusion mutuelle : non garantie •Problème : consultation-modification non atomique
29
Solutions correctes pour mettre en place l’exclusion mutuelle
Masquer les interruptions •idée : empêcher tout changement de contexte ▶ dangereux, et inapplicable sur machine multiprocesseur Approche purement logicielle •idée : programmer avec des instructions atomiques ▶ attente active = souvent inefficace à l’exécution Approche noyau : intégrer synchronisation et ordonnancement •idée : programmer avec des instructions atomiques mais les cacher dans le noyau (derrière des appels système) ▶ permet de bloquer / réveiller les threads au bon moment
30
Verrou exclusif (mutex lock)
objet abstrait = opaque au programmeur • deux états possibles : libre = unlocked ou pris = locked •offre deux méthodes atomiques lock() et unlock()
31
Quelles sont les deux méthodes atomiques pour les verrous ?
lock() et unlock() Elles sont implémentées comme appels système
32
lock(L)
Si le verrou L est libre, le prendre sinon, attendre qu’il se libère
33
unlock(L)
Libérer le verrou L • invoquer unlock() réveille **un** thread suspendu (s’il y en a)
34
Threads en attente
= état BLOCKED dans l’ordonnanceur • une file de threads suspendus pour chaque mutex
35
Interblocage (deadlock)
Situation dans laquelle deux (ou plusieurs) tâches concurrentes se retrouvent suspendues car elles s’attendent mutuellement, pour toujours …
36
Quelles sont les conditions de Coffman nécessaires à l’interblocage ?
•exclusion mutuelle •hold and wait •non-préemption •attente circulaire
37
Condition de Coffman : exclusion mutuelle
Chaque ressource critique est non partageable entre plusieurs threads
38
Condition de Coffman : hold and wait
Un thread possède déjà une ressource et attend pour en avoir une autre
39
Condition de Coffman : non-préemption
Une ressource verrouillée ne peut pas être réquisitionnée, seulement libérée volontairement
40
Condition de Coffman : attente circulaire
A attend que B libère une ressource, mais B attend que C libère une autre ressource, etc.
41
Solutions aux problèmes d’interblocages
•Prévention : garantir leur absence, par conception •Détection : reconnaître le problème quand il arrive •Résolution : régler le blocage une fois qu’il est là
42
Implémentation d’une file
Buffer/tampon circulaire (x%N) de taille constante N Pointeurs in et out [ e ] [ ] [ ] [ ] [ d ] in out
43
Scénario du Producteur-Consommateur
Deux threads communiquent via une file FIFO partagée, avec un qui produit et un qui consomme
44
Hypothèses du scénario du Producteur-Consommateur
•file partagée de taille constante •thread producteur doit attendre tant que la file est pleine •thread consommateur doit attendre tant que la file est vide
45
Problèmes des solutions à base de mutex pour le scénario du Producteur-Consommateur
•mauvaise concurrence •risques de deadlock •attente active
46
Le scénario Producteur-consommateur est insoluble avec seulement lock()/unlock()
besoin d’un mécanisme spécifique pour attendre et signaler des évènements quelconques : primitives supplémentaires (variable de condition)
47
Variable de condition
objet abstrait = opaque au programmeur •contient une file d’attente de threads suspendus •et une référence à un verrou mutex L •offre trois méthodes atomiques : wait(), signal() et broadcast()
48
Quelles sont les trois méthodes atomiques d’une variable de condition
wait(), signal() et broadcast()
49
wait(cond_var C, mutex L)
1) atomiquement : déverrouiller L, et suspendre le thread courant 2) au réveil : re-verrouiller L, puis reprendre l’exécution Au moment de l’appel, L doit être verrouillé par le thread courant sinon c’est une erreur d’exécution
50
signal(cond_var C)
Réveiller l’un des threads suspendus sur C (s’il y en a) utile quand les threads ont tous le même prédicat ▶ pas la peine de réveiller tout le monde à chaque fois (ordre de réveil non spécifié)
51
broadcast(cond_var C)
Réveiller tous les threads suspendus sur C Utile quand les threads ont des prédicats distincts ▶ pour être sûr de réveiller aussi le bon thread
52
Usage typique : la structure de moniteur
Structure de données qui offre sa propre synchronisation : permet à plusieurs threads d’accéder de manière sûre à des ressources partagées sans provoquer de conflits = mutex + variable de condition (pour la signalisation d’évènements)
53
Bonne pratique avec les variables de conditions
Always hold the lock while signalling - simplifie l’analyse des comportements possibles Always re-check predicate on wake-up - protège contre les réveils intempestifs (spurious wake-up)
54
•Pourquoi la primitive wait() est-elle atomique? vs deux notions distinctes de mutex et de CV,
Découpler mutex et CV causerait des risques à l’exécution :
55
Quelques scénarios classiques de synchronisation
Exclusion mutuelle Producteur consommateur, en VO bounded buffer problem Boucle parallèle, en VO fork-join Rendez-vous, en VO barrier
56
API POSIX et threads
#include /* opaque typedefs */ pthread_t, pthread_attr_t; int pthread_create(pthread_t *thread, pthread_attr_t *attr, void * (*function) (void *), void *arg); void pthread_exit(void *retval); int pthread_cancel(pthread_t thread); int pthread_join(pthread_t thread, void **retvalp);
57
int pthread_create(pthread_t *thread, pthread_attr_t *attr, void * (*function) (void *), void *arg);
create and start a new thread
58
void pthread_exit(void *retval);
terminate the current thread
59
int pthread_cancel(pthread_t thread);
terminate another thread
60
int pthread_join(pthread_t thread, void **retvalp);
wait for another thread to terminate
61
API POSIX et Mutex locks
#include /* opaque typedefs */ pthread_mutex_t, pthread_mutexattr_t; // create a new mutex lock int pthread_mutex_init(pthread_mutex_t *mutex, pthread_mutexattr_t *mutexattr); int pthread_mutex_lock(pthread_mutex_t *mutex); int pthread_mutex_unlock(pthread_mutex_t *mutex); // attempt to lock a mutex without blocking int pthread_mutex_trylock(pthread_mutex_t *mutex);
62
int pthread_mutex_init(pthread_mutex_t *mutex,pthread_mutexattr_t *mutexattr); int pthread_mutex_lock(pthread_mutex_t *mutex); int pthread_mutex_unlock(pthread_mutex_t *mutex);
Create a new mutex lock
63
int pthread_mutex_trylock(pthread_mutex_t *mutex);
attempt to lock a mutex without blocking
64
API POSIX et variables de condition
#include /* opaque typedefs */ pthread_cond_t, pthread_condattr_t; // create a new cond variable int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr); int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex); int pthread_cond_signal(pthread_cond_t *cond); int pthread_cond_broadcast(pthread_cond_t *cond);