difference between std::mutex and std::shared_mutex

后端 未结 3 1966
半阙折子戏
半阙折子戏 2021-02-07 08:27

I came across an std::shared_mutex in C++17. what exactly is std::shared_mutex and how it is different from std::mutex?

相关标签:
3条回答
  • 2021-02-07 09:04

    As noted in the documentation

    The shared_mutex class is a synchronization primitive that can be used to protect shared data from being simultaneously accessed by multiple threads. In contrast to other mutex types which facilitate exclusive access, a shared_mutex has two levels of access:

    • shared - several threads can share ownership of the same mutex.
    • exclusive - only one thread can own the mutex.

    Shared mutexes are usually used in situations when multiple readers can access the same resource at the same time without causing data races, but only one writer can do so.

    This has a variety of uses, but one common one is to implement a Read Write Lock where you could have multiple threads reading shared data, but only one thread exclusively writing at any time. So when you have multiple readers the mutex acts in "shared mode", but when a write is requested it changes into "exclusive mode".

    0 讨论(0)
  • 2021-02-07 09:19

    std::shared_mutex can be useful especially in cases where data structure (like DNS cache) gets rarely updated. Using a std::mutex to protect the data structure could be overly pessimistic, because it eliminates the possible concurrency in reading the data structure when it isn’t undergoing modification. Multiple threads can have a shared lock on the same std::shared_mutex at the same time.

    One such example from Anthony Williams book:

    class dns_cache
    {
        std::map<std::string,dns_entry> entries;
        mutable boost::shared_mutex entry_mutex;
    
    public:
    
        dns_entry find_entry(std::string const& domain) const
        {
            boost::shared_lock<boost::shared_mutex> lk(entry_mutex);
            std::map<std::string,dns_entry>::const_iterator const it = entries.find(domain);
            return (it==entries.end()) ? dns_entry() : it->second;
        }
    
        void update_or_add_entry(std::string const& domain,
                                dns_entry const& dns_details)
        {
            std::lock_guard<boost::shared_mutex> lk(entry_mutex);
            entries[domain] = dns_details;
        }
    };
    

    Here, function find_entry basically does the Read operation, whereas update_or_add_entry performs the Write operation.

    So, it can said that std::shared_mutex is a typical reader-writer mutex, because it allows for two different kinds of usage: exclusive access by a single “writer” thread or shared, concurrent access by multiple “reader” threads.

    0 讨论(0)
  • 2021-02-07 09:20

    A mutex is either locked or not.

    A shared_mutex is either locked exclusively, or locked shared, or not.

    Any number of clients can shared lock a shared mutex.

    If anyone has it exclusive locked, nobody else can hold any locks.

    On windows, this is the SWRLOCK type -- and in fact, this lock is typically used to implement read-write locks; many readers allowed, but writing must be exclusive.

    Here is some sample code to create two template wrappers for shared and non-shared mutexes. In one case, we have read and write operations that aquire different locks. In the other, we just have access:

    template<class T, class M=std::mutex>
    struct mutex_guarded {
      template<class F>
      auto access( F&& f ) {
        auto l = lock();
        return std::forward<F>(f)(t);
      }
      template<class F>
      auto access( F&& f ) const {
        auto l = lock();
        return std::forward<F>(f)(t);
      }
      mutex_guarded(mutex_guarded const&)=delete;
      mutex_guarded& operator=(mutex_guarded const&)=delete;
      template<class...Ts>
      mutex_guarded( Ts&&...ts ):t(std::forward<Ts>(ts)...){}
      mutex_guarded()=default;
    protected:
      mutable M m;
      T t;
      auto lock() { return std::unique_lock<M>(m); }
    };
    template<class T, class M=std::shared_mutex>
    struct shared_mutex_guarded:private mutex_guarded<T, M> {
      using base = mutex_guarded<T, M>;
      template<class F>
      auto read( F&& f ) const { return access(std::forward<F>(f)); }
      template<class F>
      auto write( F&& f ) { return access(std::forward<F>(f)); }
    
      using base::base;
    protected:
      using base::access;
      template<class F>
      auto access( F&& f ) const {
        auto l = lock();
        return std::forward<F>(f)(this->t);
      }
      using base::lock;
      auto lock() const { return std::shared_lock<M>(this->m); }
    };
    
    0 讨论(0)
提交回复
热议问题