C++ Can an atomic variable be declared inside a structure to protect those members?

  • A+
Category:Languages

Have a structure declared and defined in shared file.

Both threads create by the Windows API CreateThread() have visibility to the instance of it:

struct info {     std::atomic<bool> inUse;      string name;  }; info userStruct; //this guy shared between two threads 

Thread 1 continually locking/unlocking to write to member in structure (same value for test):

    while (1)     {         userStruct.inUse = true;         userStruct.name= "TEST";         userStruct.inUse = false;     }    

Thread 2 just reading and printing, only if it happens to catch it unlocked

    while (1)     {         while (! userStruct.inUse.load() )         {             printf("** %d, %s/n/n", userStruct.inUse.load(), userStruct.name.c_str());             Sleep(500); //slower reading         }          printf("In Use!/n");     } 

Expect to see a lot of:

"In Use!"

and every once if it gets in, when unlocked:

"0, TEST"

..and it does.

But also seeing:

"1, TEST"

If the atomic bool is 1 I do NOT expect to ever see that.

What am I doing wrong?

 


Your code is not thread safe. The atomic is atomic. But the if statement isn't !

What happens:

Thread 1                                Thread 2               Comment   while (! userStruct.inUse.load() )                             ---> InUse is false  ==> continues loop                                          inUse = true            ==> continues loop already started printf(...)  

In the worst case you could have UB due to a data race (one thread 2 modifies the string, and thread 1 reads the string during the modification).

Solution:

Since you intend to use your atomic as a lock, just use a real lock designed for this kind of synchronisation, using a std::mutex with a std::lock_guard.

For example:

struct info {     std::mutex access;      string name; };  

The first thread would then be:

while (1) {     std::lock_guard<std::mutex> lock(userStruct.access); // protected until next iteration     userStruct.name= "TEST"; }    

The second thread could then attempt to gain access to mutex in a non-blocking fashion:

while (1) {     {  //  trying to lock the mutex         std::unique_lock<std::mutex> lock(userStruct.access, std::try_to_lock);         if(!lock.owns_lock()){   // if not successful do something else             std::cout << "No lock" <<std::endl;          }         else                     // if lock was successfull         {             std::cout << "Got access:" << userStruct.name <<std::endl;         }     } // at this stage, the lock is released.     std::this_thread::sleep_for(std::chrono::milliseconds(500)); } 

Online demo

Comment

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: