MMM
MMM

Reputation: 383

(Shared) mutex in C++

I've seen an example for a shared mutex:

class MyData {
    std::vector<double> data_;
    mutable shared_mutex mut_;   // the mutex to protect data_;

public:
    void write() {
        unique_lock<shared_mutex> lk(mut_);
        // ... write to data_ ...
    }

    void read() const {
        shared_lock<shared_mutex> lk(mut_);
        // ... read the data ...
    }
};

Naturally I would have written instead:

public:
    void write() {
        mut_.lock();
        // ... write to data_ ...
        mut_.unlock();
    }

    void read() const {
        mut_.lock_shared();
        // ... read the data ...
        mut_.unlock_shared();
    }
};

Is my way also correct? And is there a difference between what I used and what was used in the example? Also, are there advantages of one over the other? Thank you!

Upvotes: 5

Views: 2395

Answers (2)

jfMR
jfMR

Reputation: 24738

Is my way also correct?

Consider what would happen if the code between the locking of the mutex and the unlocking throws an exception:

void write() {
    mut_.lock();
    // <-- exception is thrown here
    mut_.unlock();
}

The mutex then remains locked.

are there advantages of one over the other?

Yes, unique_lock<> follows the RAII idiom, and therefore unlocking of the mutex is handled automatically (i.e., by its destructor) in case of an exception:

void write() {
    unique_lock<shared_mutex> lk(mut_);
    // <-- exception is thrown
}

In case of an exception after the creation of the unique_lock<shared_mutex> object – lk – its destructor is called, and it then unlocks the associated mutex if it was locked (remember that std::unique_lock, unlike std::lock_guard, doesn't always have ownership of the lock on the associated mutex – see std::defer_lock and std::unique_lock::unlock()).

To sum up, with lock_guard/unique_lock/shared_lock, no special handling is required from your side in case of exceptions or when leaving the member function from different execution paths.

Upvotes: 12

tdao
tdao

Reputation: 17668

Raw mutex is generally avoided in favour of the RAII version unique_lock(), which is safer in two situations:

  • exception
  • premature return

Like raw pointer are generally avoided in favour of RAII version smart pointers: unique_ptr or shared_ptr.

Either case, the RAII version ensure that the mutex (or pointer) is always released when the it goes out of scope.

Upvotes: 5

Related Questions