Ma question est que la classe est-elle incluse ci-dessous pour une catégorie de fil de la file d'attente mono-enregistreur unique? Ce type de file d'attente s'appelle sans verrouillage, même s'il bloquera si la file d'attente est remplie. La structure de données a été inspirée par Mise en œuvre de Marc Gravell d'une file d'attente de blocage a > Ici à Stackoverflow. Le point de la structure consiste à permettre à un seul thread d'écrire des données sur le tampon et d'un autre fil à lire des données. Tout cela doit arriver aussi vite que possible. p> Une structure de données similaire est décrite dans un Article chez DDJ par Herb Sutter , sauf que la mise en œuvre est en C ++. Une autre différence est que j'utilise une liste liée à la vanille, j'utilise une liste liée de tableaux. P> plutôt que simplement, y compris un extrait de code, j'inclus tout ce qui compte avec un commentaire avec une licence open source permissive (licence MIT 1.0) Au cas où quelqu'un le trouve utile et veut l'utiliser (tel quel ou modifié). P> Ceci est lié à d'autres questions posées sur la dépassement de la pile de la manière de créer une bloquage des files d'attente simultanées (voir Création d'une file d'attente Blockinq dans .NET et Mise en œuvre de la file d'attente de blocage de fil de fil dans .NET ). p> Voici le code: p>
6 Réponses :
Je soupçonne que ce n'est pas le fil sûr - Imaginez le scénario suivant: P>
Deux threads Entrez Je n'ai pas exécuté ce code non plus, alors je ne sais pas si mes hypothèses sont possibles dans ce code, mais si elles sont (c'est-à-dire curseur.write de plusieurs threads lorsque cursor.write code>. Le premier devient aussi loin que la ligne nœud = nouveau noeud (x, noeud); code> dans la vraie moitié du si (actuel == tamper_size) code> Ça actuel == buffer_size code>) alors quand 1 est ajouté à actuel code>, un autre fil à venir suivrait l'autre chemin à travers la déclaration IF. Maintenant, imaginez que le fil 1 perd son tranche de temps et son fil 2 l'obtient et procède à entrer dans la déclaration IF sur la croyance erronée que l'état toujours tenu. Il aurait dû entrer dans l'autre chemin. P>
actuel == tamper_size code>), alors il pourrait bien être sujette aux erreurs de concurrence. P>
Il a mentionné que cela est destiné à une file d'attente unique auprès de l'écrivain.
Oui, peut-être que j'aurais dû faire le point plus important. Ce code n'est définitivement pas thread-coffre-fort pour plusieurs écrivains ou lecteurs.
Étant donné que je ne trouve aucune référence que le verrouillage.exchange lit ou écrit des blocs, je dirais que non. Je voudrais également poser des questions sur la raison pour laquelle vous voulez aller sans verroiement, car il donne rarement suffisamment d'avantages pour contrer sa complexité. P>
Microsoft a eu une excellente présentation au GDC 2009 à ce sujet et vous pouvez obtenir les diapositives ICI . P>
Je ne comprends pas ce que vous voulez dire, interclocked.exchange ne lit pas ou n'écrit pas de blocs. Pourriez-vous également expliquer votre logique sur la raison pour laquelle il ne s'agit pas de fil-coffre-fort? Quant à la question de la complexité et des avantages, pouvez-vous être plus précis? J'ai un exemple très concret ici que je veux savoir. Merci pour votre temps!
Premièrement, je m'interroge sur l'hypothèse de ces deux lignes de code séquentiel: Que dire que la nouvelle valeur de nœud.data [] a été engagée dans cet emplacement mémoire ? Il n'est pas stocké dans une adresse mémoire volatile et peut donc être mis en cache si je le comprends correctement? Cela ne conduira-t-il pas à une lecture «sale»? Il peut y avoir d'autres endroits la même chose est vrai, mais celui-ci se disait en un coup d'œil. P> second, code multi-threadé contenant les éléments suivants: p> Troisième, je ne comprends pas complètement l'utilisation de l'API de verrouillage ici. Peut-être que je suis fatigué et je manque juste le point; Mais je ne trouve pas le conflit de fil potentiel sur les deux threads lisant et écrire à la même variable? Il semblerait que la seule utilisation que je puisse trouver pour échanger les échanges de verrouillage serait de modifier le contenu de nœud.data [] à réparer le n ° 1 ci-dessus. P> Enfin, il semblerait que la mise en œuvre soit quelque peu trop compliquée . Est-ce que je manque le point de tout le curseur / la chose du nœud ou est-ce essentiellement la même chose que cette classe? (Remarque: je ne l'ai pas essayé et je ne pense pas que ceci est le fil en sécurité non plus, essayez simplement de réduire ce que vous pensez.) P> class ReaderWriterQueue<T>
{
readonly AutoResetEvent _readComplete;
readonly T[] _buffer;
readonly int _maxBuffer;
int _readerPos, _writerPos;
public ReaderWriterQueue(int maxBuffer)
{
_readComplete = new AutoResetEvent(true);
_maxBuffer = maxBuffer;
_buffer = new T[_maxBuffer];
_readerPos = _writerPos = 0;
}
public int Next(int current) { return ++current == _maxBuffer ? 0 : current; }
public bool Read(ref T item)
{
if (_readerPos != _writerPos)
{
item = _buffer[_readerPos];
_readerPos = Next(_readerPos);
return true;
}
else
return false;
}
public void Write(T item)
{
int next = Next(_writerPos);
while (next == _readerPos)
_readComplete.WaitOne();
_buffer[next] = item;
_writerPos = next;
}
}
1. Bon point. Ressemble à un échec clair. 2. Voici ma justification de fil.sleep (1) a) Les deux threads vont à une inclinaison complète sans interaction utilisateur B) si le thread.sleep (1) est atteint, le producteur est plus rapide que le consommateur. Donc, laissez simplement le système d'exploitation utiliser le fil du producteur pour pomper davantage de données dans la file d'attente. Une MSEC sur une machine de 3GHz devrait être de 3 millions d'instructions. Assez pour effectuer un interrupteur de contexte et pomper une quantité importante de données dans la file d'attente. 3. Interlock est utilisé avec la paranoïa. :-) 4. Voir le prochain commentaire, hors de l'espace.
4. Mon objectif avec la conception d'une liste de tampons liés était de permettre à cette file d'attente d'être petite ou grande au besoin. Si la consommation est rapide, seulement un petit tampon est nécessaire, sinon un tampon beaucoup plus grand peut être nécessaire. Mon objectif est d'avoir une chaîne entière de ces tampons travaillant en tandem. Vos commentaires sont géniaux. Je déteste aussi vraiment le filetage. Mon objectif est de créer des outils de filetage primitifs pouvant être utilisés pour construire des algorithmes parallèles sans mal de tête (par exemple, streaming des producteurs aux consommateurs sans soucis de problèmes de thread.
Je viens de réaliser qu'il y avait une constante trompeuse dans ma mise en œuvre: des tampons maximum de 4. Ce type de vaincue de la conception décrite ci-dessus. Le fabrication de 100 est plus raisonnable et la matrice peut maintenant se développer ou se rétrécir de 2k à 200k en fonction de ses besoins (en supposant un système de 32 bits).
# 1 - Vous pouvez le réparer avec fil.volatilewrite (). # 2 - n'est pas nécessaire car votre écrivain bloquera et attendra de toute façon avec la poignée d'attente. # 3 - Je supprimerais les appels étrangers à immerger, cela n'a pas de sens. Utilisez des écritures volatiles si vous voulez être paranoïaque. # 4 - Ahh, donc cela a plus de sens maintenant avoir la balance de la mémoire tampon. Je pouvais voir vouloir faire cela si votre tampon était de 0,5 Mo et que vous vouliez augmenter sans frapper le grand tas d'objets. Je pense toujours que le code global pourrait être un peu plus simple en tuant le curseur.
Re: "Un msEC sur une machine de 3Ghz doit être de 3 millions d'instructions ..." On dirait que vous n'êtes pas au courant de la nature du fil. Voir Stackoverflow.com/Questtions/508208/... Pour plus d'informations. Fondamentalement, dans un scénario par défaut, thread.sleep (1) entraînera presque toujours au moins l'équivalent de thread.sleep (15ish).
@Criss: Ce ne sera que 15MS sur OSS plus âgée. WinXP et d'autres utilisent des "ticks" pour effectuer la planification du thread, qui sont des interruptions périodiques par le matériel, défaillance à 15 ms. Les nouveaux noyaux comme Win7 utilisent une conception sans tick qui prend en charge les résolutions de microsecondes. Le sommeil (1) entraîne presque exactement 1 ms sur chaque système que je travaille.
Les échecs de recherche Microsoft doivent s'avérer être un bon outil pour tester votre mise en œuvre. P>
Et, si les échecs ne le font pas pour ya, vous pourriez essayer Jinx.
Le site Web corensiorique a été réduit pendant un moment.
Méfiez-vous du modèle de verrouillage double vérifié (comme dans un lien indiqué ci-dessus: http://www.yoda.arachsys.com/cshaarp/singleton.html ) p>
citant Verbatim de la "conception moderne C ++" de Andrei Alexandrescu P>
La CLI devrait appliquer le modèle de mémoire de la CLI .NET, peu importe que le modèle de mémoire de l'environnement SMP est. Mais oui, connaître le modèle de mémoire est essentiel pour la programmation sans verrouillage.
Le verrouillage implique des clôtures complètes (barrières de mémoire) autour de la région critique, ce n'est donc pas possible de se produire sur aucun système. Vous pouvez obtenir de faux positifs, mais une fois que vous êtes dans une région critique, vous voyez toujours les dernières données.
La présence de Sleep () code> fait une approche sans verrouillage totalement inutile. La seule raison de confronter la complexité d'une conception sans verrouillage est la nécessité d'une vitesse absolue et d'éviter le coût des sémaphores. L'utilisation de sommeil (1) défaite totalement ce but. p>
L'appel dormir () code> ne se produira que lorsque le consommateur est plus rapide que le producteur, ce qui indique une utilisation inappropriée. J'aurais pu faire une attente de spin, mais cela aurait été une utilisation inefficace des ressources. Je ne voulais pas utiliser un sémaphore car alors l'autre thread devrait la définir chaque fois qu'il pousse les données, ce qui semble trop inefficace. Je suis ouvert aux suggestions sur quoi faire à la place.
Je pense que la meilleure suggestion est d'aller avec une file d'attente de verrouillage. Je n'ai pas traversé le code dans une profondeur, mais je remarque que le mot "barrière" est absent sur cette page. Même si vous avez un algorithme «correct», la réorganisation de l'écriture, etc. pourrait toujours le faire échouer.
>> Je pense que la meilleure suggestion est d'aller avec une file d'attente de verrouillage. Pourquoi? Toutes mes recherches sur la programmation simultanée ont souligné la surutilisation des serrures comme une barrière à l'évolutivité vers des architectures massivement parallèles. >> Je n'ai pas traversé le code dans une profondeur. Alors peut-être que vous avez tort de dormir () code> dans ce contexte? >> Même si vous avez un algorithme "correct", la réorganisation de l'écriture, etc. pourrait toujours le faire échouer exactement pourquoi je faisais attention à utiliser les opérations code> interlocked code>.
@CDiggins: Ensuite, nous attendrons pendant que vous passez des jours (semaines) le tester et la vérifiant avec des échecs. J'espère que vous allez poster les résultats. Je ne pense pas que le code de code ne sera d'aucune aide ici.
Je n'ai pas exécuté le code, mais en regardant le code, cela me semble bien. Essaie. Utilisez-le sur 2 threads et forcez-le à bloquer. Si cela ne le fait pas, alors sa cassée. Poster ici sur vos résultats.
Thread.Sleep (1) Dans Reader ne permettraa de lire 1 000 articles maximum par seconde - pas bien à l'âge de 3 GHz. La file d'attente doit suivre les conventions de nommage de la file d'attente (I.e. Method Noms Enqueue, Dequeue). J'appliquerais également toutes les offres de Resharper correctes à votre code. On a l'impression que votre code ait plus de commentaires qu'il n'en a besoin car c'est assez propre et clair. Je ne peux pas dire si ça marche, cependant.
@Andrew: J'ai essayé le code sur deux threads (sur une machine à double noyau). Et aucun problème. Cependant, il pourrait toujours y avoir des bugbres méchants se cacher autour du coin. Merci de la regarder! @Konstantin - Ce sont de très bons points, merci! Je mettrai à jour la mise en œuvre comme le flux plus de commentaires.
Note latérale: Je ne suis pas sûr, alors soutient la licence par questions / réponses. Pour autant que je sache, tout ici est autorisé par la licence CC-Wiki (Attribution, Share-Ashake), y compris le code que vous avez publié ci-dessus.
@Fritan. Bon point, supprimé le code de licence de code (avec des commentaires superflus).
Quel est le point de tous les interlocked.exchandes où vous n'utilisez pas la valeur de retour? Une affectation est une opération atomique.
@Oleksandrpshenychnyyy Je ne peux rien voir à ce sujet sur MSDN thread.sleep Documentation . Avez-vous des liens vers des sources?