web-dev-qa-db-fra.com

Comment utiliser std :: atomic <>

J'ai une classe que je veux utiliser dans différents threads et je pense que je pourrai utiliser std::atomic De cette façon:

class A
{
    int x;

public:
    A()
    {
        x=0;
    }

    void Add()
    {
        x++;
    }

    void Sub()
    {
        x--;
    }     
};

et dans mon code:

  std::atomic<A> a;

et dans un fil différent:

  a.Add();

et

  a.Sub();

mais je reçois une erreur indiquant que a.Add() n'est pas connue. Comment puis-je résoudre ça?

Y a-t-il une meilleure façon de procéder?

Veuillez noter que c'est un exemple, et ce que je veux, c'est m'assurer que l'accès à la classe A est thread-safe, donc je ne peux pas utiliser

std::atomic<int> x;

Comment puis-je rendre une classe thread-safe en utilisant std::atomic?

28
mans

Vous devez rendre l'attribut x atomique, et non votre classe entière, comme suit:

class A
{
    std::atomic<int> x;

    public:
      A() {
        x=0;
      }
      void Add() {
        x++;
      }
      void Sub() {
        x--;
      }     
};

L'erreur que vous obtenez dans votre code d'origine est tout à fait normale: il n'y a pas de std::atomic<A>::Add méthode (voir ici ) sauf si vous fournissez une spécialisation pour std::atomic<A>.

Renvoi de votre modification : vous ne pouvez pas par magie faire votre class A thread safe en l'utilisant comme argument modèle de std::atomic. Pour le rendre sûr pour les threads, vous pouvez rendre ses attributs atomiques (comme suggéré ci-dessus et à condition que la bibliothèque standard lui donne une spécialisation), ou utiliser des mutex pour verrouiller vous-même vos ressources. Voir l'en-tête mutex . Par exemple:

class   A
{
  std::atomic<int>      x;
  std::vector<int>      v;
  std::mutex            mtx;

  void  Add() {
    x++;
  }
  void  Sub() {
    x--;
  }

  /* Example method to protect a vector */
  void  complexMethod() {
    mtx.lock();

    // Do whatever complex operation you need here
    //  - access element
    //  - erase element
    //  - etc ...

    mtx.unlock();
  }

  /*
  ** Another example using std::lock_guard, as suggested in comments
  ** if you don't need to manually manipulate the mutex
  */
  void  complexMethod2() {
    std::lock_guard<std::mutex> guard(mtx);

    // access, erase, add elements ...
  }

};
36
Unda

Déclarez le membre de classe x atomique, vous n'avez pas à déclarer l'objet atomique:

class A
{  
   std::atomic<int> x;
};
5
ivanw

L'opérateur . Peut être utilisé sur un objet pour appeler la fonction membre de sa classe, pas la fonction membre d'une autre classe (sauf si vous écrivez explicitement le code de cette façon).

std::atomic<A> a ;
a.Add(); // Here, a does not know what Add() is (a member function of the type parameter)
         // It tries to call Add() method of its own class i.e. std::atomic
         // But std::atomic has no method names Add or Sub

Comme le mentionne la réponse de @ivanw, faites plutôt std::atomic<int> Un membre de votre classe, puis utilisez-le.

Voici un autre exemple:

template <typename T> class A
{};

class B { public: void hello() { std::cout << "HELLO!!!"; } };

A<B> a ;
a.hello(); // This statement means that call a's hello member function
           // But the typeof(a) which is A does not have such a function
           // Hence it will be an error.
5
a_pradhan

Je pense que le problème avec les réponses ci-dessus est qu'elles n'expliquent pas ce que je pense être, au minimum, une ambiguïté dans la question, et très probablement, une erreur de développement commune.

Vous ne pouvez pas rendre un objet "atomique" car l'intervalle entre deux fonctions (d'abord "lire x" et ensuite "écrire x") provoquera une course avec d'autres utilisations. Si vous pensez avoir besoin d'un objet "atomique", vous devez soigneusement concevoir l'API et les fonctions membres à exposer maintenant pour commencer et valider les mises à jour de l'objet.

Si tout ce que vous entendez par "atomique" est "l'objet ne corrompt pas son état interne", alors vous pouvez y parvenir grâce à std::atomic<> pour les types de données simples et anciennes qui n'ont pas d'invariant entre eux (a ne dépend pas de b) mais vous avez besoin d'un verrou quelconque pour toutes les règles dépendantes que vous devez appliquer.

1
Jon Watte