Reputation: 261
Just want to know whether atomic_flag = 1;
keeps the myclass_st assignment thread-safe or not. (I am sorry that my example has so many problems, so I have changed it.)
myClass* myclass_st = nullptr;
std::atomic<int> atomic_flag;
std::mutex mtx; //err
myClass* get_instance() {
//std::unique_lock<std::mutex> lk(mtx);
if (myclass_st == nullptr) {
mtx.lock();
if (myclass_st == nullptr) {
myclass_st = new myClass();
atomic_flag = 1;
mtx.unlock(); //err
}
}
return myclass_st;
}
I know we can use static
after c11.
Maybe I should modify the code like this ?
myClass* myclass_st = nullptr;
std::atomic<int> atomic_flag;
myClass* get_instance() {
if (atomic_flag.load() == 0) {
std::unique_guard<std::mutex> lk(mtx);
if (atomic_flag.load() == 0) {
myclass_st = new myClass();
atomic_flag = 1;
}
}
return myclass_st;
}
Upvotes: 3
Views: 909
Reputation: 40882
The shown code is not threaded save, because std::mutex mtx;
is a new object each time. The std::mutex mtx;
has to be static so that it is the same mutex for each invocation of
get_instance` but even then the manual locking and unlocking of the mutex would not be valid in the current form.
EDIT after moving the std::mutex mtx;
out of the function the mutex is the same for each invocation of get_instance
. But it is still not threaded save. Multiple threads could pass the first if (myclass_st == nullptr) {
condition where myclass_st
is nullptr
. If there are e.g. more then three threads that pass the first if
then the thread that first calls lock
will set myclass_st
and release its lock on the mutex. The second thread that called lock
won't release its acquired lock, so all other threads that passed the first if
are blocked.
It has to be either:
myClass* get_instance() {
mtx.lock();
if (myclass_st == nullptr) {
myclass_st = new myClass();
atomic_flag = 1;
}
mtx.unlock();
return myclass_st;
}
Instead of manually locking and unlocking you normally want to do the locking with a lock guard with automatic storage duration (RAII idiom), because that ensures that the lock on the mutex is always released when get_instance
is left.
myClass* get_instance() {
std::lock_guard<std::mutex> lk(mtx);
if (myclass_st == nullptr) {
myclass_st = new myClass();
atomic_flag = 1;
}
return myclass_st;
}
EDIT
No neither the first nor the second example is thread save. For the second example as you show it the if (atomic_flag.load() == 0) { /** ... **/ atomic_flag = 1;}
could still be entered by two threads. So new myClass
could still be done multiple times.
Upvotes: 4
Reputation: 141975
Just want to know whether atomic_flag = 1; keeps the myclass_st assignment thread-safe or not.
No.
Maybe I should modify the code like this ?
myClass* myclass_st = nullptr; std::atomic<int> atomic_flag; myClass* get_instance() { if (atomic_flag.load() == 0) { myclass_st = new myClass(); atomic_flag = 1; } return myclass_st; }
If you intent get_instance
to be called by multiple threads, then no.
I guess you want:
myClass* myclass_st = nullptr;
std::atomic<int> atomic_flag{0};
std::mutex mtx;
myClass* get_instance() {
if (atomic_flag == 0) {
std::unique_lock<std::mutex> lk(mtx);
if (myclass_st == nullptr) {
myclass_st = new myClass();
atomic_flag = 1;
}
}
return myclass_st;
}
Upvotes: 2