58 votes

En C # serait-il préférable d'utiliser Queue.Synchronized ou lock () pour la sécurité des threads?

J'ai une File d'attente de l'objet que j'ai besoin pour s'assurer est thread-safe. Serait-il préférable d'utiliser un verrou de l'objet comme ceci:

lock(myLockObject)
{
//do stuff with the queue
}

Ou est-il recommandé d'utiliser la File d'attente.Synchronisé comme ceci:

Queue.Synchronized(myQueue).whatever_i_want_to_do();

À la lecture de la MSDN docs il est dit que je devrais utiliser la File d'attente.Synchronisé pour en faire thread-safe, mais il donne un exemple d'utilisation d'un verrou de l'objet. De l'article MSDN:

Pour garantir la sécurité des threads de La file d'attente, toutes les opérations doivent être effectuées par le biais de ce wrapper.

L'énumération d'une collection est pas intrinsèquement un thread-safe procédure. Même lorsqu'une collection est synchronisée, les autres threads peuvent toujours modifier la collection, ce qui provoque l'agent recenseur pour lever une exception. Pour garantir la sécurité des threads au cours de l'énumération, vous pouvez verrouiller la collection pendant toute la l'énumération ou intercepter les exceptions résultant des modifications apportées par d'autres les threads.

Si vous appelez Synchronisée() ne garantit pas le fil de sécurité, quel est le point? Suis-je manqué quelque chose?

47voto

Jon Skeet Points 692016

Personnellement, je préfère toujours le verrouillage. Cela signifie que vous obtenez de décider le niveau de granularité. Si vous comptez sur le Synchronisée wrapper, chaque opération est synchronisé, mais si jamais vous avez besoin de faire plus d'une chose (par exemple une itération sur l'ensemble de la collection), vous devez verrouiller toute façon. Dans l'intérêt de la simplicité, je préfère juste une chose à retenir de verrouillage de manière appropriée!

EDIT: Comme indiqué dans les commentaires, si vous pouvez utiliser des abstractions de plus haut niveau, c'est génial. Et si vous n' utiliser le verrouillage, être prudent avec lui - document à quoi vous vous attendez à être verrouillé lorsque et de l'acquisition/libérer les verrous pour une période aussi courte que possible (plus de justesse que les performances). Éviter d'appeler dans un code inconnu, tandis que le maintien d'un verrou, d'éviter imbriqués les écluses.

Dans .NET 4 il y a beaucoup plus de soutien pour les abstractions de niveau supérieur (y compris le code sans verrouillage). De toute façon, je ne le recommandent pas l'utilisation de la synchronisation des wrappers.

35voto

Greg Beech Points 55270

Il y a un problème majeur avec l' Synchronized méthodes dans l'ancienne collection de la bibliothèque, qu'ils se synchroniser à un trop faible niveau de granularité (par méthode plutôt que par unité de travail).

Il y a une course classique avec une synchronisé de la file d'attente, indiqué ci-dessous où vous vérifiez l' Count pour voir s'il est sûr de file d'attente, mais alors l' Dequeue méthode lève une exception indiquant la file d'attente est vide. Cela se produit parce que chaque opération est thread-safe, mais la valeur de Count peut changer entre le moment de la requête et à l'utilisation de la valeur.

object item;
if (queue.Count > 0)
{
    // at this point another thread dequeues the last item, and then
    // the next line will throw an InvalidOperationException...
    item = queue.Dequeue();
}

Vous pouvez écrire ceci à l'aide d'un verrou manuel autour de l'ensemble de l'unité de travail (c'est à dire de vérifier le comte et dequeueing l'élément) comme suit:

object item;
lock (queue)
{
    if (queue.Count > 0)
    {
        item = queue.Dequeue();
    }
}

Donc, comme vous ne pouvez pas retirer en toute sécurité de tout, d'un synchronisé de la file d'attente, je n'aurais pas pris la peine avec elle et qu'il suffit de l'utiliser de verrouillage manuel.

.NET 4.0 devrait avoir tout un tas de mises en œuvre correctement thread-safe collections, mais c'est encore près d'un an loin malheureusement.

15voto

Will Dean Points 25866

Il y a souvent une tension entre la demande pour les "thread-safe "collections" et l'obligation d'effectuer de multiples opérations sur la collection atomique de la mode.

Donc Synchronisée() vous offre une collection qui n'est pas le casser lui-même si plusieurs threads ajout d'éléments simultanément, mais il n'est pas comme par magie vous donner une collection qui sait que lors d'une énumération, personne d'autre ne doit le toucher.

Ainsi que l'énumération des opérations courantes comme "est-ce point déjà dans la file d'attente? Non, alors, je vais ajouter" exigent également la synchronisation qui est plus large que juste la file d'attente.

7voto

Leonidius Points 156

De cette façon, nous n'avons pas besoin de verrouiller la file d'attente pour découvrir qu'elle était vide.

 object item;
if (queue.Count > 0)
{
  lock (queue)
  {
    if (queue.Count > 0)
    {
       item = queue.Dequeue();
    }
  }
}
 

1voto

Greg Hurlman Points 10944

Il me semble évident qu’utiliser un verrou (...) {...} est la bonne solution.

Pour garantir la sécurité des threads de la file d'attente, toutes les opérations doivent être effectuées uniquement via ce wrapper.

Si d'autres threads accèdent à la file d'attente sans utiliser .Synchronized (), vous serez dans un ruisseau - à moins que tout votre accès à la file d'attente ne soit verrouillé.

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