J'ai un J'ai un est une opération de lecture qui compte la file d'attente et accède aux éléments correspondants du fil de la liste Safe Safe? P> code d'exemple :
P> system.collections.generic.list
system.collections.concurrent.concurrentqueue
private List<Object> items;
private ConcurrentQueue<int> queue;
private Timer timer;
private void callback(object state)
{
int index = items.Count;
items.Add(new object());
if (true)//some condition here
queue.Enqueue(index);
timer.Change(TimeSpan.FromMilliseconds(500), TimeSpan.FromMilliseconds(-1));
}
//This can be called from any thread
public IEnumerable<object> AccessItems()
{
foreach (var index in queue)
{
yield return items[index];
}
}
4 Réponses :
Le Concurrentqueue n'est sécurisé que par rapport aux opérations Enqueue (T) et T-dequeuse ().
Vous faites une Vous devez absolument synchroniser le Accès aux files d'attente (et la liste centrale pendant que vous y êtes) en utilisant un autre moyen de synchronisation (et je veux dire que vous pouvez également oublier Abount Concurrentqueue et utiliser une simple file d'attente ou même une liste depuis que vous ne ressusez jamais des choses). p> Faites donc quelque chose comme: p> et dans l'accessitems: p> et, Basé sur toute ma compréhension des cas dans laquelle votre application accède à la liste et aux différentes files d'attente, elle devrait être sûre à 100%. P> Tout d'abord: < un href = "http://blogs.msdn.com/b/ericlippert/archive/2009/10/19/what-is-this-thing-you-call-thread-safe.aspx" rel = "nofollow"> Quelle est cette chose que vous appelez fil-coffre-fort? par Eric Lippert P> dans votre cas particulier, je suppose que la réponse est Ce n'est pas le cas que les incohérences peuvent s'arrise dans Le contexte global (la liste réelle). p> au lieu de cela, il est possible que les lecteurs réels (qui puissent très bien "entrer en collision" avec l'écrivain unique) se retrouvent avec des incohérences en elles-mêmes (leurs propres piles signifiant: variables locales de toutes les méthodes, paramètres et leur partie isolée logiquement du tas)). P> La possibilité de tels incohérences "par thread" (le nième thread veut apprendre le nombre d'éléments de la liste et découvre que la valeur est 39404999 Bien que, en réalité, vous ne ajoute que 3 valeurs) suffisent à déclarer que, d'une manière générale, cette architecture est Je vous suggère d'utiliser ReaderWriterLockSlim classe.
Je pense que vous trouverez que cela correspond à vos besoins: p> fin de gros modification h3>
Articles [Index] est la seule ligne que je suis inquiet. Dans cet exemple spécifique, cela peut-il lancer une exception ou renvoyer le mauvais article?
Parlons-nous de la modification que j'ai faite à votre méthode code> Accessitems code>? Si vous voulez dire que vous voulez dire SnapShot [Index], alors, oui ... Vous ne jouez pas du tout. Pourquoi utilisez-vous réellement la file d'attente?
Hei .. ne vous inquiétez pas, j'ai eu une solution, vérifiez la nouvelle édition de ma réponse
Non, je demande mon code d'origine. éléments [index] code> est le seul endroit où il pourrait y avoir un problème
S'il vous plaît vérifier l'édition que j'ai faite à ma réponse. Je ne suis pas sûr que c'est le seul endroit .. c'est un endroit. Ça c'est sûr. Les modifications sont mineures (ajoutez simplement l'objet ReaderWrriterLockSlim et gardez votre code avec celui-ci - Modification de la nature Itératrice de la 2e méthode (plus de rendement de rendement)
BTW: On dirait que vous ne ressemblez jamais à votre file d'attente. Faites-vous tout cela juste pour assurer la commande dans la liste? (Il est déjà assuré) ou vous ne mentionnez pas tout ce que vous êtes sur le point de faire avec les deux collections?
Quel autre endroit pourrait-il y avoir un problème? Dans mon code actuel, il y a plusieurs files d'attente, pas seulement. Chaque file d'attente contient différents indices en fonction de la logique. Chaque file d'attente est donc une vue différente de la liste. La méthode Accessitems comporte un autre paramètre qui spécifie quelle file d'attente à utiliser. Les files d'attente ne sont jamais dequelles.
Laissez-nous Continuez cette discussion en chat
Non parce que vous lisez et écrivez à / depuis le même objet simultanément. Ceci n'est pas documenté pour être en sécurité, vous pouvez donc vous assurer qu'il est sûr. Ne le faites pas. P>
Le fait que c'est en fait dangereux à partir de .NET 4.0 signifie rien, BTW. Même si c'était en sécurité selon le réflecteur, cela pourrait changer à tout moment. Vous ne pouvez pas compter sur la version actuelle pour prédire les versions futures. strong> p>
N'essayez pas de vous échapper avec des tours comme ça. Pourquoi ne pas simplement le faire d'une manière évidemment sûre? P>
En tant que note latérale: Deux rappels de la minuterie peuvent exécuter en même temps, votre code est donc doublement brisé (multiples écrivains). N'essayez pas de tirer des tours avec des fils. strong> p>
Accepter avec le point "ne tirez pas". Je planifie la minuterie pour le prochain rappel uniquement lorsque le rappel actuel est terminé. Deux rappels ne peuvent donc pas exécuter en même temps dans ce cas.
est une opération de lecture qui compte la file d'attente et accède aux éléments correspondants du fil de la liste? P> blockQuote>
est-ce
documenté fort> comme étant le fil de sécurité? p> Si non, alors
il est stupide de le traiter comme un fil sûr, même si c'est dans cette mise en œuvre par accident fort>. La sécurité du fil doit être par design em>. P> Le partage de la mémoire à travers les threads est une mauvaise idée en premier lieu; Si vous ne le faites pas, vous n'avez pas à vous demander si l'opération est en sécurité. p>
Si vous devez le faire, utilisez une collection conçue pour l'accès à la mémoire partagée. P>
Si vous ne pouvez pas faire cela, alors utiliser un verrou em>. Les serrures sont bon marché si non compris. p>
Si vous avez un problème de performance, car vos serrures sont affirmées tout le temps, résolvez ce problème em> en modifiant votre architecture de threading plutôt que d'essayer de faire des choses dangereuses et stupides comme le code Low-Lock. Personne n'écrit correctement le code à verrouillage à l'exception d'une poignée d'experts. (Je ne suis pas l'un d'entre eux; Je n'écris pas non plus de code à verrouillage.) P>
Même si la liste est redimensionnée lorsqu'elle est indexée, j'accélère qu'à un élément qui existe déjà, de sorte qu'il n'a pas de problème, qu'il s'agisse de la lecture de l'ancien tableau ou de la nouvelle matrice. P> blockQuote>
C'est la mauvaise façon d'y penser. La bonne façon de penser est: p>
Si la liste est redimensionnée, les structures de données internes de la liste sont mutées. Il est possible que la structure de données interne soit mutée dans une forme incohérente à mi-chemin de la mutation, qui sera rendue compatible au moment de la fin de la mutation. Par conséquent, mon lecteur peut voir cet état incohérent d'un autre fil, ce qui rend le comportement de mon programme total imprévisible em>. Cela pourrait s'écraser, cela pourrait aller dans une boucle infinie, cela pourrait corrompre d'autres structures de données, je ne sais pas, car Je suis courant qui suppose un état cohérent dans un monde avec un État incohérent em>. p> blockQuote>
Bref, précis et au point (alors que le mien était long, non structuré et incompréhensible :))
@ERIC Merci pour la réponse et je n'utiliserai pas ce code dans la production. Mais ai-je raison de croire que le seul problème avec ce code est dans le redimensionnement de la liste? Ou y a-t-il d'autres problèmes tels que l'accès au champ de réseau potentiellement sorti de la part de la forach?
@ K.M.: Lorsque vous conjecture, il existe des problèmes de cohérence potentiels. Rien n'empêche des lectures non volatiles de comptage et les matrices d'être déplacées vers l'avant et vers l'arrière dans le temps les uns des autres.
@ERICLIPPERT Dites-vous qu'un index incorrect pourrait être inséré dans la file d'attente, même si le rappel n'est jamais appelé simultanément de différents threads? Ce serait très surprenant pour moi.
Utilisation du code qui n'est pas conçu pour un accès multithread comme s'il était souvent i> résulte des résultats très surprenants.
@ K.m.: Je dis que vous avez deux endroits, on stocke la valeur de la propriété Count et un élément de tableau. Vous lisez et écrivez les deux emplacements sur un fil et lisez à la fois sur un autre fil. (N'oubliez pas que le lecteur doit savoir que l'indice est inférieur au nombre de comptes afin qu'il puisse jeter si ce n'est pas le cas.) Je ne vois rien qui empêche les lectures et écrit de devenir sans ordonnance les uns des autres, qui m'inquiète. Quels arrêtnent-ils d'être incompatibles avec si la valeur a été écrite dans le tableau?
C'est le fil-lafish. La déclaration de forach utilise la méthode ConcurrentQueue.getenumerator (). Qui promet: p>
L'énumération représente un instantané instantané du contenu de la file d'attente. Il ne reflète aucune mise à jour de la collection après que Getenumerator a été appelée. L'énumérateur est sûr d'utiliser simultanément avec des lectures et écrites à la file d'attente. P> blockQuote>
Ce qui est une autre façon de dire que votre programme ne va pas exploser au hasard avec un message d'exception inscultable comme le type que vous obtiendrez lorsque vous utilisez la classe de la file d'attente. Méfiez-vous des conséquences cependant, implicite dans cette garantie est que vous recherchez une version rassis de la file d'attente. Votre boucle ne sera pas en mesure de voir aucun élément ajouté par un autre thread après que votre boucle ait commencé à exécuter. Ce genre de magie n'existe pas et est impossible à mettre en œuvre de manière cohérente. Que vous préférez ou non que votre programme est quelque chose que vous devrez réfléchir et ne peut être deviné de la question. Il est assez rare que vous puissiez l'ignorer complètement. P>
Votre utilisation de la liste <> est cependant totalement dangereuse. P>
Merci pour l'info. J'ai commis une erreur dans ma réponse (en supposant que, puisque la plupart des implémentations Getenumerator généralement utilisés ne font pas ce que @Hans Passant a déclaré à propos de la mise en œuvre de Concurrentqueue, il est préférable de simplement synchroniser les choses vous-même et commencer à utiliser une file d'attente régulière au lieu de Concurrentqueue).
Oui, l'utilisation du code est conçue pour gérer la relâche. Je voulais être sûr que l'accès à la liste n'entraîne pas une exception ou un élément erroné en cours de lecture.
Il lit de la liste lorsque la liste est écrite (clairement dangereuse). Il écrit également de multiples threads (plusieurs invocations de rappels simultanées sont possibles avec les minuteries).
C'est dangereux lorsque vous utilisez la file d'attente. Concurrentqueue fournit des garanties de concurrence qui le rendent en sécurité.
Oh, d'accord, je n'ai pas vu ça. Aucune idée de ce qui essaie de faire, mais cela a certainement l'air de Borken.
Quelle classe de minuterie utilisez-vous?
et non, ce n'est pas le fil sûr.
ajout code> à
éléments code> en la lecture d'un comportement indéfini.
System.threading.Timer. Techniquement, je conviens que c'est un comportement indéfini, mais je ne peux penser à une mise en œuvre raisonnable de la liste qui causerait un problème ici. Je suis inquiet des choses comme la cohérence de cache que je ne comprends pas trop bien.
1) CACHE COHÉRENCE PASSEZ DÉJÀ 2) Considérez une implémentation qui stocke l'ancien tableau dans une variable locale, remplace le champ avec la nouvelle matrice, puis copie les éléments.
@CODESINCHAOS Pouvez-vous élaborer comment la cohérences de cache casses cela?