c++c++17mutexlock-guard

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?


Solution

  • 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];
        }
    };