I'm working on a small project of mine that involves the use of multiple threads.
The C++ STD library offsers different ways to prevent undefined behavior
or data races
from accessing the same memory block:
std::atomic
std::mutex
withstd::lock_guard
Now, from my understanding, you can use both of them and the result would be pretty much the same. Both locks a resource when used by a thread and prevents another from accessing it.
Though, my question is:
When should I prefer to use std::atomic
over std::mutex
and when should I use std::mutex
over std::atomic
?
Take this simple class as an example. I have three way of using this class to prevent data races
:
Using std::atomic<Buffer>
class Buffer {
private:
int m_Data;
public:
void Write(int data) {
m_Data = data;
}
int Read() {
return m_Data;
}
}
int main() {
std::atomic<Buffer> buffer;
buffer.store(Buffer());
RunThread_0(&buffer, 10);
RunThread_1(&buffer, 20):
}
Using class member std::atomic<int> m_Data
class Buffer {
private:
std::atomic<int> m_Data;
public:
void Write(int data) {
m_Data.store(data);
}
int Read() {
return m_Data.load();
}
}
int main() {
Buffer buffer = Buffer();
RunThread_0(&buffer, 10);
RunThread_1(&buffer, 20):
}
Using std::mutex
and std::lock_guard
class Buffer {
private:
int m_Data;
std::mutex m_DataMutex;
public:
void Write(int data) {
const std::lock_guard<std::mutex> guard(m_DataMutex);
m_Data = data;
}
int Read() {
const std::lock_guard<std::mutex> guard(m_DataMutex);
return m_Data
}
}
int main() {
Buffer buffer = Buffer();
RunThread_0(&buffer, 10);
RunThread_1(&buffer, 20):
}
Note: RunThread_0
and RunThread_1
do what you'd expect: they both call the Buffer::Write
method to change the value of member Buffer::m_Data
.
Final thoughts
I always used std::mutex
and std::lock_guard
, but I feel like std::atomic
is 'easier' to use as you don't have to worry about missing a lock, whereas if you use std::mutex
you can accidentally miss an std::lock_guard
statement and thus making a data race possible.
So, is there an advantage in using std::atomic
over std::mutex
or is it just a design matter?