web-dev-qa-db-fra.com

Est-ce que std :: atomic <std :: string> fonctionne correctement?

Je lis à travers "C++ Concurrency in Action" d'Anthony Williams et dans le chapitre 5, qui parle du nouveau modèle de mémoire multithreading et des opérations atomiques, et il déclare:

Pour utiliser std::atomic<UDT> pour certains UDT définis par l'utilisateur, ce type doit avoir un opérateur d'affectation de copie trivial.

Si je comprends bien, cela signifie que nous pouvons utiliser std::atomic<UDT> si ce qui suit retourne vrai:

std::is_trivially_copyable<UDT>::value

Par cette logique, nous ne devrions pas pouvoir utiliser std::string comme argument de modèle pour std::atomic et le faire fonctionner correctement.

Cependant, le code suivant se compile et s'exécute avec la sortie attendue:

#include <atomic>
#include <thread>
#include <iostream>
#include <string>

int main()
{
    std::atomic<std::string> atomicString;

    atomicString.store( "TestString1" );

    std::cout << atomicString.load() << std::endl;

    atomicString.store( "TestString2" );

    std::cout << atomicString.load() << std::endl;

    return 0;
}

Est-ce un cas de comportement indéfini qui se comporte juste comme prévu?

Merci d'avance!

45
Thomas Russell

La norme ne spécifie pas de spécialisation de std::atomic<std::string>, Donc le générique template <typename T> std::atomic<T> S'applique. 29.5 [atomics.types.generic] p1 déclare:

Il existe un modèle de classe générique atomique. Le type de l'argument modèle T doit être copiable de manière triviale (3.9).

Rien ne dit que la mise en œuvre doit diagnostiquer les violations de cette exigence. Donc, soit (a) votre utilisation de std::atomic<std::string> Invoque un comportement indéfini, soit (b) votre implémentation fournit std::atomic<std::string> Comme extension conforme.

En regardant la page MSDN pour std::atomic<T> ( http://msdn.Microsoft.com/en-us/library/vstudio/hh874651.aspx ), il mentionne explicitement l'exigence que T être trivialement copiable, et il ne dit rien de spécifique sur std::atomic<std::string>. S'il s'agit d'une extension, elle n'est pas documentée. Mon argent est sur un comportement indéfini.

Plus précisément, la version 17.6.4.8/1 s'applique ( avec merci à Daniel Krügler de m'avoir remis au clair ):

Dans certains cas (fonctions de remplacement, fonctions de gestionnaire, opérations sur les types utilisés pour instancier des composants de modèle de bibliothèque standard), la bibliothèque standard C++ dépend des composants fournis par un programme C++. Si ces composants ne répondent pas à leurs exigences, la norme n'impose aucune exigence à la mise en œuvre.

std::string Ne répond certainement pas à l'exigence std::atomic<T> Voulant que le paramètre de modèle T soit trivialement copiable, donc la norme n'impose aucune exigence à l'implémentation. En tant que problème de qualité d'implémentation, notez que static_assert(std::is_trivially_copyable<T>::value, "std::atomic<T> requires T to be trivially copyable"); est un diagnostic facile pour détecter cette violation.


2016-04-19 Update: Je ne sais pas quand le changement s'est produit, mais VS2015 Update 2 diagnostique maintenant std::atomic<std::string>:

erreur C2338: atomic nécessite que T soit trivialement copiable.
47
Casey

Non, c'est un comportement indéfini. De plus, comme std :: string n'est pas trivialement copiable, le compilateur conforme aurait dû émettre "au moins un message de diagnostic":

29.5 Types atomiques

Il existe un modèle de classe générique atomique. Le type de l'argument modèle T doit être trivialement copiable (3.9).

1.4 Conformité de la mise en œuvre

- Si un programme contient une violation d'une règle diagnostiquable, [...] une implémentation conforme doit émettre au moins un message de diagnostic.

8
user3228152

Pourquoi pensez-vous que cela fonctionnera "correctement" quand plusieurs threads essaient de lire/écrire le std::atomic<std::string>?

C'est du C++, vous êtes définitivement autorisé à vous tirer une balle dans le pied. Si vous souhaitez utiliser un type qui ne satisfait pas aux exigences que vous êtes libres d'utiliser, le compilateur "peut" (pas!) Vous arrêtera, mais vous commencerez à voir un comportement étrange/inexplicable à un moment donné lorsque plusieurs threads essaient de lire/écris la chaîne.

Cette exigence est de garantir l'atomicité des lectures et écritures, si l'objet n'est pas trivialement copiable, alors visualisez cette scène: la chaîne contenait "Old Value". 1 Writer émet .store ("New Data"), maintenant il y a un autre thread qui émet .load () sur la même variable, maintenant sans propriété trivial_copyable, le thread du lecteur peut voir "Nld Value" ou "New Value" etc. Il ne peut pas être mis à jour atomiquement, d'où les résultats étranges.

Étant donné que l'exemple que vous avez publié est un code séquentiel, cela ne se produit pas.

0
GadaaDhaariAvi