2008-10-17 7 views
6

J'ai une classe personnalisée qui implémente ICollection, et cette classe est en lecture seule, ie. IsReadOnly renvoie true (par opposition à l'utilisation du mot-clé readonly) et toutes les fonctions qui normalement modifieraient les données dans la collection lancent InvalidOperationException. Maintenant, étant donné une telle construction, et un survol rapide sur les problèmes de sécurité de thread lors de l'implémentation ICollection (en particulier ICollection.IsSynchronized et amis), j'ai trouvé cette solution rapide et sale.ICollection, collections en lecture seule et synchronisation. Est-ce correct?

bool ICollection.IsSynchronised { get{ return true; } } 
object ICollection.SyncRoot { get{ return new Object(); } } 

Maintenant, compte tenu des exemples dans MSDN, cela ne causera pas de threads différents pour verrouiller correctement, car ils deviennent différents objets de SyncRoot. Étant donné qu'il s'agit d'une collection en lecture seule, est-ce un problème? Y at-il des problèmes de mémoire/GC avec le retour new Object()? D'autres problèmes que vous pouvez voir avec cette implémentation?

Répondre

4

Oui c'est un problème dans certains cas. Même si la collection est en lecture seule et ne peut pas être modifiée, les objets que la collection référence ne sont pas en lecture seule. Ainsi, si les clients utilisent le SyncRoot pour effectuer le verrouillage, ils ne seront pas thread-safe lors de la modification des objets référencés par la collection.

Je recommande d'ajouter:

private readonly object syncRoot = new object(); 

à votre classe. Renvoyez-le comme le SyncRoot et vous êtes prêt à partir.

1

Je suppose que le problème serait que les clients utilisent votre racine de synchronisation pour verrouiller non seulement votre collection, mais quelque chose d'autre. Supposé qu'ils mettent en cache la taille de la collection - ou peut-être "quel sous-ensemble de cette collection correspond à un prédicat" - ils supposeraient raisonnablement qu'ils pourraient utiliser votre SyncRoot pour protéger à la fois votre collection et leur autre membre.

Personnellement, je n'utilise pratiquement pas SyncRoot, mais je pense qu'il serait judicieux de toujours retourner la même chose.

+0

La collection est en effet immuable, il n'y a aucun moyen de la modifier (sauf pour creuser dans la réflexion bien sûr), donc tout ce genre de choses devrait être garanti ne devraient-ils pas? –

+0

Considérez que vos clients peuvent ne pas savoir qu'ils obtiennent une collection immuable. Évidemment, ils n'essaient pas de le modifier (ou ils auront déjà une erreur), mais ils pourraient bien être verrouillés pour s'assurer qu'ils n'échoueraient pas s'ils recevaient une collection mutable. –

+0

Oui, d'accord, ils appellent Syncroot, obtiennent un objet contre lequel se verrouiller, un objet que personne d'autre n'aura jamais, donc le verrou est inutile. Mais devrions-nous vraiment tenir d'autres discussions inutilement? –

2

Il semble très étrange de retourner un objet différent à chaque fois ... en fait, je n'utilise que très rarement l'approche SyncRoot, comme souvent j'ai besoin de synchroniser entre plusieurs objets etc, donc un objet séparé est plus significatif .

Mais si les données sont vraiment immuables (en lecture seule), pourquoi ne pas simplement retourner false à IsSynchronized? Re GC - un tel objet serait typiquement de courte durée et serait recueilli dans GEN0. Si vous avez un champ avec un objet (pour le verrou), cela durera aussi longtemps que la collection, mais probablement ne fera pas mal de toute façon ...

Si vous avez besoin d'une serrure, je serais tenté de juste un:

private readonly object lockObj = new object(); 

vous pouvez également utiliser une approche paresseuse seulement « nouvelle » en cas de besoin, ce qui en fait une certaine quantité de sens que si vous ne vous attendez pas vraiment à quiconque de demander le verrouillage de synchronisation (par renvoie false à IsSynchronized).

Une autre approche courante consiste à retourner "ceci"; il garde les choses simples, mais les risques entrent en conflit avec un autre code utilisant votre objet comme un verrou pour un but sans rapport. Rare, mais possible. C'est en fait l'approche que [MethodImpl] utilise pour synchroniser.

+1

Cela ne veut-il pas dire que l'objet n'est pas sûr pour le fil, alors qu'en fait, il l'est? –

+0

Peut-être; IMO, toute la logique autour des collections synchronisées est un peu borked - il est rare que vous vouliez la sécurité des threads pour une opération * simple * - vous voulez normalement vérifier une valeur et ensuite faire quelque chose d'autre. Je soupçonne que le code le plus commun ne vérifiera jamais cette propriété de toute façon ... –

+0

Je pense que la raison principale de ceci est de mettre une instruction verrouillée autour des boucles foreach, donc la boucle ne gâche pas si quelqu'un modifie la collection pendant que vous êtes énumérant par dessus. –

Questions connexes