9
votes

Problèmes de commande de la mémoire

J'essaie un support C ++ 0x et il y a un problème que je suppose ne devrait pas être là. Soit je ne comprends pas le sujet ou GCC a un bug.

J'ai le code suivant, initialement x et y est égal. Filf 1 incrémente toujours x d'abord puis incréments y . Les deux sont des valeurs entières atomiques, il n'y a donc aucun problème avec l'incrément du tout. Le fil 2 vérifie si le x est inférieur à y et affiche un message d'erreur si oui.

Ce code échoue parfois, mais pourquoi? Le problème ici est probablement une réorganisation de la mémoire, mais toutes les opérations atomiques sont cohérentes de manière séquentielle par défaut et je ne suis pas explicitement relaxé de ces opérations. Je compose ce code sur x86, ce qui autant que je sache ne devrait pas avoir de problèmes de commande. Pouvez-vous s'il vous plaît expliquer ce que le problème est? xxx

Le résultat peut être visualisé Ici .


8 commentaires

En réalité, il s'agit d'une mise en œuvre expérimentale de C ++ 0x, la seconde est donc possible, mais je crois que le premier est plus probable: p


Le code posté ci-dessus produira toujours "erreur" , ( x sera toujours supérieur ou égal à y ) est-ce que tu voulais?


Pourquoi si (x


Quelle est la commande de mémoire par défaut pour l'incrément? Un magasin garantit-il une libération? [Mon expérience avec le modèle de mémoire C ++ 0x est limitée]


@Paul: J'ai immédiatement accepté avec vous, mais vous pensions et je pensais "si c'est pas ce qu'il teste, impression " erreur ". Mais le code est si ce > est , impression it.thread 2 peut voir des paires du formulaire (n, n) et (n + 1, n) . Dans les deux cas, x est faux. Il y a une séquence qui le déclenche, cependant. @james: Ouais.


"Quelle est la commande de mémoire par défaut pour l'incrément?" - Séquentiellement cohérent est par défaut.


@confucius: j'ai pensé; Un ordre détendu par défaut serait terrible.


@Gman: Vous avez raison bien sûr. Je pense que j'ai passé beaucoup trop longtemps à regarder le code pendant une journée.


4 Réponses :


11
votes

Le problème pourrait être dans votre test: xxx

Le thread pourrait évaluer x et ne pas contourner pour évaluer y jusqu'à ce que plus tard.


4 commentaires

Je suppose que c'est le problème, la question est pourquoi? En réalité, les opérations atomiques cohérentes séquentiellement doivent l'empêcher, ne doivent-ils pas?


Merci beaucoup! Le problème est une séquence non spécifiée d'évaluation d'expression, si simple :)


@confucius: Bien que votre scénario puisse avoir une dépendance à la commande selon laquelle les variables pourraient être lues, la question plus générale est que la lecture de 2 instances atomiques différentes n'est pas atomique.


Bien sûr, je pense que dans la situation actuelle, une ligne trompeuse dans le code était (x



12
votes

Il y a un problème avec la comparaison:

int yval = y;
int xval = x;
if (xval < yval) { /* ... */ }


1 commentaires

Merci! C'est ça. Désolé les gars, ne peuvent pas plus, https est bloqué dans le bureau et je ne peux pas me connecter :(



-3
votes

Premièrement, je suis d'accord avec "Michael Burr" et "James McNellis". Votre test n'est pas juste et il y a une possibilité légitime d'échouer. Cependant, même si vous réécrivez le test comme "James McNellis" suggère que le test peut échouer.

première raison pour cela est que vous n'utilisez pas la sémantique volatile , d'où le compilateur peut effectuer des optimisations à votre code (qui sont censés être corrects dans un boîtier à un seul fileté).

mais même avec volatile Votre code n'est pas garanti de fonctionner.

Je pense que vous ne comprenez pas complètement le concept de la réorganisation de la mémoire . En réalité, la réorganisation de la mémoire / écriture peut se produire à deux niveaux:

  1. compilateur peut échanger l'ordre des instructions de lecture / écriture générée.
  2. CPU peut exécuter des instructions de lecture / écriture de la mémoire dans l'ordre arbitraire.

    Utilisation de volatile empêche le (1). Cependant, vous n'avez rien fait pour empêcher (2) - l'accès à la mémoire de la réorganisation du matériel .

    Pour empêcher cela, vous devez mettre des instructions spéciales clôture de mémoire dans le code (désigné pour la CPU, contrairement à volatile qui est uniquement pour le compilateur uniquement).

    en x86 / x64 Il y a de nombreuses instructions de clôture de mémoire différentes. Aussi toutes les instructions avec Serrure Sémantique par défaut des problèmes de mémoire complète de la mémoire.

    Plus d'informations ici:

    http://fr.wikipedia.org/wiki/memory_barrier


1 commentaires

Valdo - Pas besoin d'utiliser des volatiles ici, car les barrières de mémoire par défaut générées par des opérations atomiques C ++ 0x empêchent les deux (1) et (2).



4
votes

de temps en temps, x sera envelopper sur 0 juste avant y wraps autour de zéro. À ce stade, y sera légitimement supérieur à x .


2 commentaires

A pris un coup de feu à la modifier. Il peut également être noté que le débordement signé conduit à un comportement indéfini, bien que le débordement non signé s'envole comme prévu.


Se mettre d'accord. Le trop-plein n'était pas censé arriver, cela a été fait juste pour le test, mais cela pourrait être un problème aussi. Merci.