Mulithreading C++20 : lecteurs-rédateur
On propose une solution au problème des lecteurs-rédacteur en C++.
Description
Là encore il s’agit d’un problème de partage de ressource entre 2 processus, mais cette fois-ci de manière non symétrique, puisque les processus auront des priorités différentes.
Rôle des rédacteurs
Les rédacteurs doivent accéder à la ressource de manière exclusive et donc attendre si au moins un lecteur la possède.
Rôles des lecteurs
Les lecteurs peuvent :
- accéder à la ressource si aucun rédacteur ne la possède;
- se partager la ressource tant qu’au moins l’un d’entre eux y a accés (au détriment des rédacteurs).
Hypothèses
On suppose que :
- la zone partagée est de taille fixe;
- les vitesses des processus sont quelconques.
Analyse
Communication inter-processus sur la disponibilité de données
La communication entre les processus va être similaire au cas précédent. Les processus des différentes catégories doivent pouvoir se signaler entre eux de la possilité ou non d’accéder à la ressource.
Une fois que la ressource en cours de lecture, le rédacteur ne pourra y accéder que lorsque tous les lecteurs souhaitant y accéder auront terminé. À l’inverse, quand un rédacteur posséde la ressource, aucun lecteur n’y accédera. La notion de priorité sera gérée par le premier lecteur accédant (qui bloquera le rédacteur), ainsi que par le dernier (qui libérera la ressource).
Ce mécanisme est réalisé à l’aide d’un sémaphore qui sera pris par le premier lecteur et relâché par le dernier.
Section critique
Dans ce mécanisme, il faut assurer la cohérence de la valeur du nombre de lecteurs qui sera modifié par chaque lecteur au moment où il accéde à la ressource, et au moment où il la libère. Pour verrouiller sa modificiation on utilisera un mutex.
Code
Les données partagées
La ressource partagée est ici representée par un entier. La variable n_readers
contient le nombre de lecteurs en train d’accéder à la ressource.
// This represents the ressource shared between process, irl it would be a file or a device
int shared_ressource = 0;
// Number of readers reading
unsigned int n_readers = 0;
// Mutex to protect the number of readers variable
std::mutex readers_mutex;
// Ressource is free at start
std::binary_semaphore sem_write{1};
Le rédacteur
Le rédacteur ne peut pas agir tant que le sémaphore indiquant la possibilté d’écrire ne peut peut être acquis. Une fois acquis, il modifie la valeur partagée.
void writer(void* args) {
while(true) {
sem_write.acquire();
shared_ressource++;
sem_write.release();
}
}
Les lecteurs
Le premier lecteur prend l’accés à la ressource, et le dernier la libère.
void reader(void* args) {
while(true) {
{
// The first reader takes the semaphore
std::lock_guard<std::mutex> lock(readers_mutex);
if (++n_readers) sem_write.acquire();
}
// Reads...
int value = shared_ressource;
(void) value;
{
// The last one releases the semaphore
std::lock_guard<std::mutex> lock(readers_mutex);
if (--n_readers == 0) sem_write.release();
}
}
}
Résultat
Le code est disponible sur le dépôt Github