Mars
Mars

Reputation: 2572

Any proper way to achieve locks in a situation like this?

I have an array of objects that I want to operate on in threads, but I also want to be able to access at times. This feels like a hacky way to achieve my goal, but is there a better way to do something like this?:
*the basic goal is to have 2 locks. One that allows all individual threads to work concurrently while blocking access from the array until they are all finished, and one that allows shuts down access from the thread to ensure that no objects are touched by other threads while a function runs.

atomic<int> inThreadCount;
atomic<int> arrayLock;
map<string, MyObj*> myMap;
mutex mu1;
class MyObj{
    mutex mu2;
    int myInt;
    public: 
    void update(bool shouldLowerCount){
        mu2.lock();
        myInt++;
        if (shouldLowerCount)
            inThreadCount--;
        mu2.unlock();
    }
}
//Some operation that requires all threads to finish first
//and doesn't allow threads to access the objects while running
void GetSnapshot(){ 
    mu1.lock();
    arrayLock++;
    while (inThreadCount > 0)
        Sleep(0);
    map<string, MyObj *>::iterator it = myMap.begin();
    auto t = time(nullptr);
    auto tm = *localtime(&t);
    cout << put_time(&tm, "%d-%m-%Y %H-%M-%S") << endl;
    for( ; it != myMap.end(); ++it){
        cout << it->first << ":" << it->second->counter);
    }
    arrayLock--;
    mu1.unlock();
}

void updateObject(MyObj* myObj){
    while (arrayLock > 0)
        Sleep(0);
    inThreadCount++;
    async(std::launch::async, myObj->update(true));
}

PS, I realize that there is a tiny window of opportunity for error between Sleep() and arrayLock/inThreadCount++. That's part of the problem I want to solve!

Upvotes: 0

Views: 299

Answers (1)

Persixty
Persixty

Reputation: 8589

I think you're asking for a shared mutex. A shared mutex (or read-write mutex) allows many threads to lock an object in parallel while also allowing one thread at a time to lock it exclusively.

In simple terms, if a thread requests shared access it is granted unless a thread holds the object exclusively. A thread is granted exclusivity when the object isn't held (shared or exclusively) by any other thread.

It's common use is read-write exclusivity. See shared access to read and exclusive access to write. That's valid because a data race can only occur when two ore more threads access the same data and at least one of them is a write operation. Multiple readers is not a data race.

There are normally overheads in implementing a shared lock as opposed to an exclusive lock, and the model only normally helps where there are 'many' readers that read 'frequently' and write operations are 'infrequent'. What 'many', 'frequent' and 'infrequent' mean depends on the platform and problem in hand.

That's exactly what a shared mutex is for. C++17 supports that out of the box with std::shared_mutex but I noticed the question is tagged C++11.

Some implementations have offered that for some time (it's a classic locking strategy) Or you can try boost::shared_mutex<>.

NB: One of the challenges in a shared-lock is to avoid live-lock on the writer. If there are many readers that read frequently it can be easy the writer to get 'locked out' indefinitely and never progress (or progress very slowly). A good shared lock will provide some guarantee of the writer eventually getting a turn. That may be absolute priority (no writers allowed to start after a thread starts waithing

Upvotes: 4

Related Questions