How to use std::atomic<>

mans picture mans · Jun 10, 2015 · Viewed 57.1k times · Source

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 ?

Answer

Unda picture Unda · Jun 10, 2015

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

};