EXTENSION DES LISTES GÉNÉRIQUES (DESIGN PATTERN "DECORATEUR")

cs_Yxion Messages postés 219 Date d'inscription jeudi 6 juillet 2006 Statut Membre Dernière intervention 7 septembre 2009 - 2 oct. 2007 à 10:43
cs_yoannd Messages postés 305 Date d'inscription lundi 7 janvier 2002 Statut Membre Dernière intervention 10 août 2011 - 25 mars 2009 à 13:51
Cette discussion concerne un article du site. Pour la consulter dans son contexte d'origine, cliquez sur le lien ci-dessous.

https://codes-sources.commentcamarche.net/source/44247-extension-des-listes-generiques-design-pattern-decorateur

cs_yoannd Messages postés 305 Date d'inscription lundi 7 janvier 2002 Statut Membre Dernière intervention 10 août 2011 7
25 mars 2009 à 13:51
très juste TheManu ;)
TheManu Messages postés 8 Date d'inscription jeudi 26 juillet 2007 Statut Membre Dernière intervention 11 mai 2010
24 mars 2009 à 15:16
Sympa ta classe !
Par contre :
- Dans 'Clear()', pourquoi ne pas utiliser 'RemoveAt(0)' plutôt que 'Remove(this[0])' ?
De plus (et de toute façon), si un autre thread supprime entre le 'while' et le 'Remove' (ou le 'this[0]') un élément ET que celui-ci été le dernier t'es marron ! Je sais que c'est plus pour le tutorial mais je ne pense pas que tu puisses (pour simplifier l'écriture) te passer d'un 'lock' englobant la boucle d'effacement et le lancement d'évènements. Pour simplifier il faudrait (je pense) l'appel en boucle (et dans un 'lock') d'un "RemoveAtNonSafeThread(0)"...
- De même : l'énumération sur la liste (grâce à 'GetEnumerator') n'est pas "thread-safe" !
flashkel Messages postés 5 Date d'inscription vendredi 4 juin 2004 Statut Membre Dernière intervention 23 novembre 2007
23 nov. 2007 à 10:45
Sauf qu'ici il fait un lock(base) ce qui revient (dans le contexte d'une implémentation d'une IList<T>) à ce que tu conseilles MALKUTH mais le conseil est judicieux pour d'autre cas.
cs_Malkuth Messages postés 268 Date d'inscription samedi 22 février 2003 Statut Membre Dernière intervention 24 avril 2013 4
17 nov. 2007 à 18:35
Petite précision sur le lock, il est déconseillé de faire un
lock(this){ ... }

Car d'autre partie du programme pourais vouloir posé un vérou
sur l'objet pour des raison totalement différente que celle de
l'ajout d'un nouvel item dans la liste, pour ma part, je déclare
un un objet suplémentaire dans ma classe pour géré le lock :
private object modif_LOCK = new object();

On utilise ensuite :
lock(this.modif_LOCK) { ... }

On peut aussi noté que lock n'est pas une instruction du framework
à proprement parlé mais un artifice du compilateur C# qu'on peut
interprété comme ceci :
try
{
Sytem.Threading.Monitor.Enter(this.modif_LOCK);
...
}
finally
{
Sytem.Threading.Monitor.Exit(this.modif_LOCK);
}

Il est possible de dans certain cas d'utilisé d'autre classe que
Sytem.Threading.Monitor pour géré les vérroux comme par example :
ReaderWriterLock
qui permet de diférencié les opérations de lectures(qui ne se lock pas entre elles) des opérations d'écriture (qui sont exclusive).
cs_yoannd Messages postés 305 Date d'inscription lundi 7 janvier 2002 Statut Membre Dernière intervention 10 août 2011 7
2 oct. 2007 à 14:55
Je viens de compulser l'aide msdn, et il s'avère que le lock correspond à ce que je voulais faire, alors que le thread.BeginCriticalRegion fait autre chose.

Petites explications :
lock : bloque l'accès à une ressource et traite toutes les instructions qu'il contient en une fois. Autrement dit, il y a exclusion mutuelle : ce mot clé permet de s'assurer qu'un thread ne se trouve pas dans la partie de code concernée en même temps qu'un autre thread.

thread.BeginCriticalRegion : permet de spécifier au sein d'un thread que la région entourée par le begin et le end ne peut pas être annulée sans mettre en péril les données de l'application. On indique ainsi qu'il y a une région critique entre le begin et le end. Définition officielle de la msdn : "Avertit un hôte que l'exécution est sur le point d'entrer dans une région de code dans laquelle les effets d'un abandon de thread ou d'une exception non gérée peuvent compromettre d'autres tâches dans le domaine d'application.".

Voila pour ce point, cela veut dire qu'il me faut modifier un peu ma source :-)
billou_13 Messages postés 860 Date d'inscription jeudi 4 mars 2004 Statut Membre Dernière intervention 19 août 2014 29
2 oct. 2007 à 13:37
Merci beaucoup pour tes réponses. Elles me semblent honorables et avoir une raison d'être (si c'est pas beau ça ^^)

Je prends plus que note de ton exemple et je n'hésiterais pas à le suivre dans mes prochains développements.

PS: désolé si j'ai pas mis de note, comme je t'ai dit, je ne l'ai lu qu'en diagonale pour l'instant. Je vais creuser aussi ce point dans quelques heures/jours... LoL
cs_yoannd Messages postés 305 Date d'inscription lundi 7 janvier 2002 Statut Membre Dernière intervention 10 août 2011 7
2 oct. 2007 à 13:22
Salut Billou_13,

Alors pour répondre à tes questions :

- Il me semble que Lock permet de poser un vérou sur un objet précis, interdisant la lecture/écriture sur une instance d'un objet tant que le vérou n'est pas levé. Si le thread 1 pose un lock sur l'objet "Obj", le thread 2 sera mis en attente jusqu'à ce que le Thread 1 lève son lock. Quand au Thread.BeginCriticalRegion, il permet de ne traiter un ensemble d'instruction qu'en une seule fois, comme s'il s'agissait d'un seul et même bloc. Pour tout t'avouer, je ne suis pas très callé sur ce point là moi non plus, et je pense que je vais creuser ce point dans quelques heures/jours.

- Sinon, j'ai effectivement dérivé moi aussi de l'objet List<T>, au début, mais la solution présentée ici (et qui implémente l'interface IList) a pour particularité de pouvoir ajouter de nouvelles responsabilités à un objet List<T>. Concrètement, si tu as un objet dont tu ne dispose pas du code, et qui est de type List, tu peux le "décorer" (c'est le nom du pattern) avec l'objet EvtList. Ton objet de liste aura alors des responsabilités supplémentaires, celle-ci étant de lever un évènement lors d'un Add ou lors d'un Remove. Bon concrètement, il est possible de dériver de List<T>, mais il faut mieux implémenter IList<T> (implémenter une interface plutôt que dériver d'une classe semble être un principe assez admis chez les architectes, dites-moi si je me trompe)...

voilou :-)
billou_13 Messages postés 860 Date d'inscription jeudi 4 mars 2004 Statut Membre Dernière intervention 19 août 2014 29
2 oct. 2007 à 11:56
Salut,

Merci pour ton explication, elle a l'air fortement intéressante. Je l'ai lu en diagonale (faut quand même bosser un peu ^^) pour l'instant et je me pencherais dessus plus tard.

Beaucoup de choses m'intéresse au premier abord et je voudrais te poser deux petites questions (pour l'instant) :

- je voudrais me rassurer en te demandant quelle est la différence entre lock(this) { } et Thread.BeginCriticalRegion() ? Pour moi, cela viendrait juste du fait que tout l'objet est bloqué pour le lock tandis que pour l'autre cas, juste le morceau de code à exécuter est bloqué. Ai-je bien compris ? (j'avoue pêcher encore toutes ces histoires ^^)

- Pour ma part, lorsque je veux faire un peu la même chose que toi, j'avais pour habitude d'hériter de la classe List<T> directement et non pas IList<T>. C'était la première solution que j'avais trouvé et je m'en suis bien contenter. Alors je me dis que peut être j'ai fait des conneries ou pas... Peux-tu m'expliquer pourquoi ton choix va vers l'interface IList<T> plutot que la classe List<T> ?
cs_Yxion Messages postés 219 Date d'inscription jeudi 6 juillet 2006 Statut Membre Dernière intervention 7 septembre 2009
2 oct. 2007 à 11:56
Oui, ca va éviter certains de mettre une sale note sans expliquer pourquoi.
cs_yoannd Messages postés 305 Date d'inscription lundi 7 janvier 2002 Statut Membre Dernière intervention 10 août 2011 7
2 oct. 2007 à 11:51
lol

N'empèche, c'est pas mal ce système, parceque parfois, tu en as qui mettent des salles notes sans se dévoiler... un peu facile, je trouve :-)

En tout cas merci pour le 10 ^^
cs_Yxion Messages postés 219 Date d'inscription jeudi 6 juillet 2006 Statut Membre Dernière intervention 7 septembre 2009
2 oct. 2007 à 11:48
Oui, j'avais pas vu, heureusement que j'ai pas mis une sale note, j'aurais été vendu aussitôt... lol
cs_yoannd Messages postés 305 Date d'inscription lundi 7 janvier 2002 Statut Membre Dernière intervention 10 août 2011 7
2 oct. 2007 à 11:39
Et bien si cette solution de liste peut te rendre des services, ma foi ^^
Il est vrai en plus que l'explication du thread-safe manquait cruellement !
Au fait, les petites étoiles à côté de ton pseudo dans le second message, ça veut dire quoi ? C'est pour indiquer que c'est toi qui as mis la note (Si c'est le cas, merki) ?
cs_Yxion Messages postés 219 Date d'inscription jeudi 6 juillet 2006 Statut Membre Dernière intervention 7 septembre 2009
2 oct. 2007 à 11:24
Ben merci pour cette réponse.
Ta source est mieux et à beaucoup plus d'intéret par le fait qu'elle soit thread-safe.
Je voulais juste faire un genre de ListEvent<T>... une List<> avec un événement add remove.. bien trop léger pour en faire un post... quand je dis que tu m'as coupé l'herbe sous le pieds, c'est que ces temps si j'utilise souvent des IList avec events, et j'avais dans l'idée d'en faire quelque chose de plus Générique... après 4 collections de suites, on se dis qu'on se prends la tete pour pas grand choses lol.
cs_yoannd Messages postés 305 Date d'inscription lundi 7 janvier 2002 Statut Membre Dernière intervention 10 août 2011 7
2 oct. 2007 à 11:09
Salut,

Les Thread.BeginCriticalRegion(); permettent ici d'avoir des listes qui sont thread-safe, en quelque sorte.

Je ne sais pas si tu as vu, mais on lève une exception si un même élément est ajouté 2 fois à la liste.
Imaginons 2 threads qui utilisent tous les 2 la méthode Add en même temps, et avec le même objet à ajouter. S'ils arrivent tous les 2 en même temps à la ligne "if (InternalList.Contains(item))", ils vont passer le test avec succès, et ajouter 2 fois (1 fois par thread) le même élément à la liste.

Thread.BeginCriticalRegion(); permet d'éviter ce souci en indiquant à la CLR que tout ce qui ce trouve entre le begin et le end ne doit être compris que comme une seule et même instruction "unitaire".

Pour ce qui est de l'index dans le eventargs, pas de souci, il peut très bien être ajouté sans trop de problèmes, mais là encore, le Thread.BeginCriticalRegion(); est important. Si un thread ajoute un élément, et que l'autre en retire un en même temps, alors les index envoyés dans les event args ne seront pas forcément justes au moment ou ils seront récupérés par l'écouteur de l'évènement si on est pas thread-safe...

Et sinon, désolé de t'avoir coupé l'herbe sous le pied, mais si tu publies tout de même ta source, préviens-moi, je serais ravi d'y jeter un coup d'oeuil ;-)
cs_Yxion Messages postés 219 Date d'inscription jeudi 6 juillet 2006 Statut Membre Dernière intervention 7 septembre 2009
2 oct. 2007 à 10:43
Bien, tu me coupes pour ainsi dire l'herbe sous le pieds, je voulais déposer une source un peux dans ce genre la... mais pas tout à fais quand même.
Juste une question, le "Thread.BeginCriticalRegion();" et celui de fin, c'est pas que je critique, mais il servent à quoi... je n'ai jamais utilisé ca.
Ce qui peux etre intéressant, ce serait d'avoir l'index dans l'eventargs, ca m'est arrivé d'en avoir besoin pour séléctionner le précédent ou suivant après une suppression.
Rejoignez-nous