PC SOFT

FOROS PROFESIONALES
WINDEVWEBDEV y WINDEV Mobile

Inicio → WINDEV 2025 → Multiple reader/single writer implantation
Multiple reader/single writer implantation
Iniciado por Youri JUTEAU, 05,dic. 2018 11:04 - 2 respuestas
Miembro registrado
36 mensajes
Publicado el 05,diciembre 2018 - 11:04
Bonjour,
Si vous avez comme moi à accéder à des zones mémoires, tableau en lecture, écriture par plusieurs threads, vous vous êtes surement cassé la tête avec des section critiques bloquantes et non performante.

J'ai fait une mise en oeuvre avec une classe qui implémente le patron décrit par Wikipédia: https://en.wikipedia.org/wiki/Readers%E2%80%93writer_lock

Je vous soumet donc cette classe qui peut être utiliser de n'importe où en instanciant un objet local soit en lecture ou en écriture.

Tout se passe dans le constructeur, destructeur.

Le gros avantage est qu'il peut y avoir autant de lecteurs que l'on veut mais un et un seul écrivain. Et lorsque l'écrivain prends la main, on est assuré que les lecteurs sont sortis et qu'ils vont attendre la fin de l'écriture.

ETypeAccèsMémoire est un Enumération
AccèsLecture=1
AccèsÉcriture=2
FIN
clAccèsMémoire est une Classe
CONSTANTE
PREFIXE_MUTEX_="MUTEX_ZONEMEMOIRE_"
PREFIXE_SIGNAL_="SIGNAL_ZONEMEMOIRE_"
FIN
PRIVÉ
m_sZoneMémoire est une chaîne
m_eType est un ETypeAccèsMémoire
GLOBAL
mg_taLecteur est un tableau associatif d'entiers//Clé: nom de la zone à protéger, nombre de lecteurs
mg_taMutex est un tableau associatif de chaîne//Clé: nom de la zone à protéger
mg_taÉcriture est un tableau associatif de booléen//Clé: nom de la zone à protéger, état si en écriture
FIN

Procedure Constructeur(sZoneMémoire est une chaîne, eType est un ETypeAccèsMémoire)
m_sZoneMémoire=ChaîneFormate(sZoneMémoire,ccSansAccent+ccSansEspace+ccMajuscule)
m_eType=eType
SI mg_taLecteur[m_sZoneMémoire]..Vide ALORS
GénérerAccès(m_sZoneMémoire)
FIN
SELON m_eType
CAS AccèsLecture
//Wikipédia:
//Lock m (blocking).
//While w:
// wait c, m[a]
//Increment r.
//Unlock m.
MutexDébut(PREFIXE_MUTEX_+m_sZoneMémoire)
TANTQUE mg_taÉcriture[m_sZoneMémoire]
MutexFin(PREFIXE_MUTEX_+m_sZoneMémoire)
SignalAttend(PREFIXE_SIGNAL_+m_sZoneMémoire)
MutexDébut(PREFIXE_MUTEX_+m_sZoneMémoire)
FIN
mg_taLecteur[m_sZoneMémoire]++//ici le thread a le lock
SI EnModeTest() ALORS
TraceConstruit("Lecture %1 compteur: %2",PREFIXE_MUTEX_+m_sZoneMémoire,mg_taLecteur[m_sZoneMémoire])
FIN
MutexFin(PREFIXE_MUTEX_+m_sZoneMémoire)//Release du lock
//La lecture de l'accès mémoire peut commencer car la condition write est désactivé et le compteur>0
CAS AccèsÉcriture
//Wikipédia:
//Lock m (blocking).
//While w:
// wait c, m
//Set w to true.
//While r > 0:
// wait c, m
//Unlock m.
MutexDébut(PREFIXE_MUTEX_+m_sZoneMémoire)
TANTQUE mg_taÉcriture[m_sZoneMémoire]
MutexFin(PREFIXE_MUTEX_+m_sZoneMémoire)
SignalAttend(PREFIXE_SIGNAL_+m_sZoneMémoire)
MutexDébut(PREFIXE_MUTEX_+m_sZoneMémoire)
FIN
mg_taÉcriture[m_sZoneMémoire]=Vrai//ici le thread a le lock
TANTQUE mg_taLecteur[m_sZoneMémoire]>0
MutexFin(PREFIXE_MUTEX_+m_sZoneMémoire)
SignalAttend(PREFIXE_SIGNAL_+m_sZoneMémoire)
MutexDébut(PREFIXE_MUTEX_+m_sZoneMémoire)
FIN
SI EnModeTest() ALORS
TraceConstruit("Écriture %1",PREFIXE_MUTEX_+m_sZoneMémoire)
FIN
MutexFin(PREFIXE_MUTEX_+m_sZoneMémoire)
//L'écriture dans la zone mémoire peut commencer car la condition write est activée
FIN
Procedure Destructeur()
SI m_sZoneMémoire<>"" ALORS
SELON m_eType
CAS AccèsLecture
//Wikipédia: Releasing a read lock is done by decrementing r and signalling c if r has become zero (both while holding m)
//On doit reprendre le lock pour décrémenter et signaler si on est arrivé à zéro
MutexDébut(PREFIXE_MUTEX_+m_sZoneMémoire)
mg_taLecteur[m_sZoneMémoire]--
SI mg_taLecteur[m_sZoneMémoire]=0 ALORS
SignalOuvre(PREFIXE_SIGNAL_+m_sZoneMémoire)
FIN
SI EnModeTest() ALORS
TraceConstruit("Fin lecture %1 compteur: %2",PREFIXE_MUTEX_+m_sZoneMémoire,mg_taLecteur[m_sZoneMémoire])
FIN
MutexFin(PREFIXE_MUTEX_+m_sZoneMémoire)
CAS AccèsÉcriture
//Wikipédia: Releasing the write lock means setting w to false and broadcasting on c (again while holding m).
//On doit reprendre le lock pour désactiver la condition d'écriture
MutexDébut(PREFIXE_MUTEX_+m_sZoneMémoire)
mg_taÉcriture[m_sZoneMémoire]=Faux
SignalOuvre(PREFIXE_SIGNAL_+m_sZoneMémoire)
SI EnModeTest() ALORS
TraceConstruit("Fin écriture %1",PREFIXE_MUTEX_+m_sZoneMémoire)
FIN
MutexFin(PREFIXE_MUTEX_+m_sZoneMémoire)
FIN
FIN


Vous n'avez donc qu'a instancier un objet de lecture avec eType=AccèsLecture et avec eType=AccèsÉcriture pour l'écriture juste avant les portions de codes correspondantes. Vous devez bien sur utiliser la même chaîne sZoneMémoire dans le constructeur pour contrôler le même élément mémoire.

--
Youri JUTEAU
Montréal
Miembro registrado
44 mensajes
Publicado el 07,diciembre 2018 - 13:46
Bonjour,

Merci pour le partage, cela est intéressant.
Cependant, il manque la fonction GénérerAccès()
ca devrait être ?
mg_taLecteur[m_sZoneMémoire] = 0
Miembro registrado
36 mensajes
Publicado el 07,diciembre 2018 - 15:07
Oui possible. Je vais l'ajouter pour voir. Mais je fonctione avec 12 threads qui se partage des dizaines d'endroits tant en lecture qu'en écriture et ça fonctionne comme un charme.

Par contre, le mieux est de déclarer dans le code globale du projet les zones mémoires que l,on prévoit utiliser en multi-threads

clAccèsMémoire.GénérerAccès("gMonTableau"))


Car il semble que la fonction mg_taLecteur[m_sZoneMémoire]..Vide n'est pas thread safe. Ell est appelé dans le constructeur mais si il n'y a pas d'ajout (fait dans l'init du projet), ça ne cause pas de pb.


Ensuite on peut juste créer un objet avant les zone affectées, le constructeur/destructeur fait le reste

Ex:
clMonAccès est un clAccèsMémoire("gMonTableau", ETypeAccèsMémoire.AccèsLecture)
//Code en lecture multiple ici sur gMonTableau



Pour être certain que le destructeur s'éxécute dès que la lecture est terminée.

SI Vrai ALORS
clMonAccès est un clAccèsMémoire("gMonTableau", ETypeAccèsMémoire.AccèsLecture)
//Code en lecture multiple ici sur gMonTableau
FIN


--
Youri JUTEAU
Montréal