Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Should I use lock_guard, scoped_lock or unique_lock in this situation?

I have read many of the questions already answered that relate to this but none of them gave me a clear understanding of which I should be using when I have multiple writers but a single reader. The code below is a contrived example of what I'm talking about.

struct StateInfo {
    long wd{};
    uint32_t perc{};
};

class Blah 
{
    const int numDevices = getDevices();
    std::shared_mutex sharedMutexSI_;
    vector<StateInfo> stateInfo;
public:

    Blah() : stateInfo(numDevices){};

    void writers(StateInfo &newSi, const int i)
    {
        std::shared_lock<std::shared_mutex> _MULTIPLE(sharedMutexSI_);
        stateInfo[i] = newSi;
    }

    StateInfo reader(const int i)
    {
        std::lock_guard<std::shared_mutex> _EXCLUSIVE(sharedMutexSI_);
        return stateInfo[i];
    }
};

The situation is that multiple writers may simultaneously update the stateInfo vector but never the same item in the vector as i is unique to each thread. A single reader thread may at any time try to read any of the vector items.

  1. Is the above code correct in avoiding race conditions?

  2. Is lock_guard the right one to use or should I use scoped_lock or unique_lock?

like image 292
poby Avatar asked Jul 25 '20 07:07

poby


People also ask

What is the difference between lock_guard and unique_lock?

A lock_guard always holds a lock from its construction to its destruction. A unique_lock can be created without immediately locking, can unlock at any point in its existence, and can transfer ownership of the lock from one instance to another.

Is lock_guard deprecated?

There exist valid use cases where it is desirable for scoped_lock to accept variadic template parameter packs which may be empty. And the empty case should not lock anything. And that's why lock_guard isn't deprecated.

What is the benefit of using std :: unique_lock?

std::unique_lock This class guarantees an unlocked status on destruction (even if not called explicitly). Therefore it is especially useful as an object with automatic duration, as it guarantees the mutex object is properly unlocked in case an exception is thrown.

Does unique_lock automatically unlock?

@Prab, just to give the explanation: unique_lock() is automatically released when the destructor is called meaning it is exception safe and that it automatically unlocks when you leave scope.


1 Answers

To summarize what was already written in comments:

Yes, the code is correct. However, it may be inefficient, because it disallows reading from any array element while writing to another array element. You might want to do more fine-grained synchronization by using a mutex for each array element.

class Blah 
{
    const int numDevices = getDevices();
    std::vector<std::mutex> mutexes;
    std::vector<StateInfo> stateInfo;
public:

    Blah() : mutexes(numDevices), stateInfo(numDevices){}

    void writers(StateInfo &newSi, const int i)
    {
        std::lock_guard<std::mutex> guard(mutexes[i]);
        stateInfo[i] = newSi;
    }

    StateInfo reader(const int i)
    {
        std::lock_guard<std::mutex> guard(mutexes[i]);
        return stateInfo[i];
    }
};
like image 129
anatolyg Avatar answered Sep 22 '22 15:09

anatolyg