How to use std::atomic<>

69,716

Solution 1

You need to make the x attribute atomic, and not your whole class, as followed:

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

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

The error you get in you original code is completely normal: there is no std::atomic<A>::Add method (see here) unless you provide a specialization for std::atomic<A>.

Referring your edit: you cannot magically make your class A thread safe by using it as template argument of std::atomic. To make it thread safe, you can make its attributes atomic (as suggested above and provided the standard library gives a specialization for it), or use mutexes to lock your ressources yourself. See the mutex header. For example:

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 ...
  }

};

Solution 2

Declare the class member x as atomic, then you don't have to declare the object as atomic:

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

Solution 3

The . operator can be used on an object to call its class's member function, not some other class's member function (unless you explicitly write the code that way).

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

As the answer by @ivanw mentions, make std::atomic<int> a member of your class instead and then use it.

Here is another example:

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.

Solution 4

I think the problem with the answers above is that they don't explain what I think is, at a minimum, an ambiguity in the question, and most likely, a common threaded development fallacy.

You can't make an object "atomic" because the interval between two functions (first "read x" and then later "write x") will cause a race with other uses. If you think you need an "atomic" object, then you need to carefully design the API and member functions to expose now to begin and commit updates to the object.

If all you mean by "atomic" is "the object doesn't corrupt its internal state," then you can achieve this through std::atomic<> for single plain-old-data types that have no invariant between them (a doesn't depend on b) but you need a lock of some sort for any dependent rules you need to enforce.

Share:
69,716
mans
Author by

mans

Updated on August 16, 2020

Comments

  • mans
    mans almost 4 years

    I have a class that I want to use in different threads and I think I may be able to use std::atomic this way:

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

    and in my code:

      std::atomic<A> a;
    

    and in a different thread:

      a.Add();
    

    and

      a.Sub();
    

    but I am getting an error that a.Add() is not known. How can I solve this?

    Is there any better way to do this?

    Please note that it is an example, and what I want is to make sure that access to class A is thread-safe, so I can not use

    std::atomic<int> x;
    

    How can I make a class thread-safe using std::atomic ?

  • mans
    mans almost 9 years
    what if I want to use a std:;vector<A> and access different member of vector in different threads?
  • Morwenn
    Morwenn almost 9 years
    @mans You can safely access different elements of an std::vector from multiple threads as long as you don't access the same element from several threads. That's a guarantee of the standard library.
  • mans
    mans almost 9 years
    @Morwenn But it seems that I can not write. If I read in one thread and write in another thread, the system crashes, Am I wrong?
  • Morwenn
    Morwenn almost 9 years
    @mans If you read the same memory location that you are writing into, this is indeed a data race.
  • mans
    mans almost 9 years
    @Morwenn How can I guard it using std:;atomic<> (if I can)?
  • Cogwheel
    Cogwheel almost 9 years
    That's like asking "how can I unscrew this philips head screw with a socket wrench?" a screwdriver and a wrench are both tools to turn things, but one of them is not the right tool for the job. You need a proper mutex or some other more heavy duty synchronization construct in order to protect an entire vector.
  • Luke B.
    Luke B. almost 9 years
    I would sugest std::lock_guard instead of manually locking and releasing.
  • Virus721
    Virus721 over 7 years
    @LukeB.Is the idea behind lock_guard to rely on the automatic variables's destructiors to be called when exiting the scope in order to release the lock, just like with RAII ?
  • Unda
    Unda over 7 years
    @Virus721 Yes, that's the idea behind std::lock_guard.