3 votes

Accès thread-safe à une collection statique

Je crée une classe qui aura une association clé-valeur de quelque sorte. Actuellement, j'ai quelque chose de similaire à ce qui suit :

private static Dictionary> PropertyCache { get; set; }

Est-ce la bonne façon de mettre en œuvre une approche thread-safe ? Pour une raison quelconque, quelque chose me dérange concernant l'effet de static sur le Dictionary dans ce cas. Si c'est correct, y a-t-il une manière incorrecte qui peut être démontrée afin que je puisse l'éviter dans le reste de mon code.

De plus, devrait-il être en lecture seule (je vais seulement ajouter/enlever des éléments de la collection) ?

Si cela fait une différence, la propriété sera toujours déclarée private.

Merci d'avance

Bien que marqué C#, les réponses en VB sont acceptées, je suis "bi-lingue" en quelque sorte

EDIT:

Suite à la discussion avec Jon Skeet dans les commentaires de sa réponse, l'utilisation sera la suivante :

// Je ferai ceci
PropertyCache.Add(typeof(string), new List());
PropertyCache.Remove(typeof(string));

// Je ne ferai jamais ceci
PropertyCache = new Dictionary>();

Je ne vais jamais non plus itérer sur la collection, seulement y accéder par clé.

3voto

Jon Skeet Points 692016

Vous devriez probablement le rendre en lecture seule sauf si vous avez besoin de modifier la valeur de la variable, oui.

Si vous utilisez .NET 4, vous devriez envisager d'utiliser ConcurrentDictionary - sinon, je rédigerais probablement des accesseurs qui acquièrent simplement un verrou pour chaque accès. Bien que vous puissiez utiliser ReaderWriterLockSlim, etc., je choisirais personnellement l'approche la plus simple et sûre pour commencer.

Remarquez qu'itérer sur le contenu du dictionnaire de manière thread-safe sera délicat - j'espère que vous n'en avez pas besoin cependant.

2voto

Erik Noren Points 3211

Rendre un membre static augmente la probabilité d'avoir un accès inter-file plus qu'un membre d'instance. Rappelez-vous que static ne signifie pas que quelque chose est sûr pour être utilisé par plusieurs threads, cela signifie que l'objet est partagé entre plusieurs instances d'un objet dans lequel il est défini.

Les collections qui implémentent l'interface ICollection peuvent être castées pour accéder à un SyncRoot que vous pouvez utiliser pour locker un objet afin de garantir qu'un seul thread à la fois peut exécuter une série d'instructions qui causeraient un changement dans la collection. Utiliser SyncRoot pour les applications multithread

lock(((ICollection)myObject).SyncRoot)
{
    //Code qui devrait être exécuté par un seul thread concurrent
    //Ceci inclut les ajouts/instructions/suppressions/itérations/vidages/etc.
}

En plus de cette méthode manuelle (ou plus ancienne), il existe des objets concurrents disponibles dans .NET 4 qui font essentiellement la même chose mais avec quelques autres vérifications spéciales. Pour la plupart, ces objets sont optimisés pour les performances autant que possible pour des objets entièrement sécurisés. Si vous utilisez un ensemble d'actions très contrôlé et restreint sur l'objet (vous avez 1 méthode d'ajout, 1 méthode de suppression et ne parcourez jamais la collection entière mais accédez simplement à des entrées spécifiques et connues), vous pourriez utiliser l'exemple plus léger de lock() ci-dessus pour obtenir de meilleures performances.

Vous le remarquerez surtout lors de l'ajout de plusieurs objets à une collection en une seule fois. En utilisant les objets concurrents, chaque opération d'ajout est atomique avec le verrouillage et le déverrouillage. Si vous exécutez ceci dans une boucle serrée, vous perdez un peu de performances à acquérir des verrous à plusieurs reprises. Si un autre thread essaie de lire, la contention pour l'accès devient un peu plus élevée. Si vous utilisez l'instruction de verrouillage vous-même, vous pouvez simplement acquérir le verrou, ajouter les objets dans une boucle rapide et serrée, puis relâcher le verrou. Les threads voulant accéder à l'objet attendront un peu plus longtemps mais dans l'ensemble, les opérations devraient se terminer plus rapidement. Gardez également à l'esprit que les différences sont généralement si faibles qu'elles ne valent pas vraiment la peine et tomberaient dans la catégorie de l'optimisation prématurée dans presque tous les cas.

0voto

driis Points 70872

Ce que vous avez montré n'est en aucun cas sécurisé pour le multithreading. Si plusieurs threads peuvent insérer ou supprimer des éléments en même temps, vous avez un problème potentiel.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X