I need some help for understanding general basics of shared resource access in C++ multithreading.
When I need some variable that must be accessible for several threads, I declare it as atomic, or synchronize operations with it by mutex.
But what if some function/method required pointer to type, and I must protect my variable(shared resource) for read/write in multiply threads. I know that in C and C++ pointers is passed by value, but anyway that confused me.
Question. Can I need to lock pointer to shared resource too:
void foo (int * pi = nullptr)
{
//{..} some ops
static std::mutex mtx;
std::unique_lock<std::mutex> lock(mtx); // lock mutex BEFORE ptr check
if(pi){
++(*pi); // dereference ptr's shared resource
}
lock.unlock();
//{...} some ops
}
OR lock only for resource itself:
void foo (int * pi = nullptr)
{
//{..} some ops
static std::mutex mtx;
if(pi){ // check if ptr is not null, is this thread-safe ?
std::unique_lock<std::mutex> lock(mtx); // lock mutex ONLY for access shared resource itself,
// not for pointer that refers to it
++(*pi); // dereference ptr's shared resource
lock.unlock();
}
//{...} some ops
}
Here some dumb "synthetic" example call with shared resource:
int main(){
int __i = 0;
std::thread t([&__i](){
for(;;){
foo(&__i);
std::this_thread::sleep_for(std::chrono::milliseconds(500));
}
});
for(;;){
foo(&__i);
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
//std::cout << __i << std::endl;
}
t.join();
}
P.S. 'int' type in this example only for clarity.
In your example, you don't need to lock before checking if pi is a nullptr since nothing from the outside can change what pi is pointing at. Your second example is therefore sufficient.