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