I was trying to figure out the data race theme, and I made this code. Here we work with the shared element wnd
. I thought that by putting lock in the while loop, I would prohibit the th1
thread from working with wnd
, but this did not happen and I see an unobstructed output of the th1
thread.
#include <iostream>
#include <thread>
#include <mutex>
#include <chrono>
int main()
{
bool wnd = true;
std::mutex mutex;
std::unique_lock<std::mutex> lock(mutex, std::defer_lock);
std::thread th1([&]() {
int i = 0;
while (true)
{
i;
lock.lock();
if (wnd)
std::cout << i << " WND TRUE" << std::endl;
else
std::cout << i << " WND FALSE" << std::endl;
lock.unlock();
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
});
while (true)
{
lock.lock();
std::this_thread::sleep_for(std::chrono::milliseconds(2000));
if (wnd)
wnd = false;
else
wnd = true;
lock.unlock();
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
th1.join();
return 0;
}
To tell the truth, I was hoping to see that the th1
thread stops printing for 2 -seconds at a time when the main thread is inside the lock section.
CodePudding user response:
You are not using the mutex and specially std::unique_lock
properly.
#include <iostream>
#include <thread>
#include <mutex>
#include <chrono>
int main()
{
bool wnd = true;
std::mutex mutex;
std::thread th1{[&]() {
for (int i = 0; i<10000; i)
{ std::unique_lock<std::mutex> lock(mutex);
std::cout << i << "\tWND\t " << std::boolalpha << wnd << std::endl;
};
}};
for (int i = 0; i<30; i)
{ std::unique_lock<std::mutex> lock(mutex);
std::this_thread::sleep_for(std::chrono::milliseconds(2000));
wnd = !wnd;
};
th1.join();
}
std::unique_lock
uses its constructor operand as a resource whose acquisition is lock
and release is unlock
. It is designed to use RAII
as a means of guaranteeing correct lock/unlock sequences on mutexes. a defered lock only means the mutex is not locked at the begining of lifespan of the std::unique_lock
and it is not the usual use case. You can manually lock/unlock the mutex, but that generally leads to less maintainable, more error-prone code.
Keep in mind that if the threads involved are not racing over the ownership of the mutex, neither waits for the other; in your original prorgram, the worker thread did not touch the mutex. But in the program above, both threads are competing to lock the mutex; winner gets a chance to continue what he wants, and the loser has to wait until the mutex is unlocked - so that he can take its ownership.