C++11 实现读写锁 read_write_mutex

£可爱£侵袭症+ 提交于 2019-11-29 09:40:58

读写锁 read_write_mutex

对于一个数据操作,简单的分可以分为读和写。

但是经常会遇到多人同时访问一个数据的情况:

  1. 多人读
  2. 多人写
  3. 有人读,有人写

处理这种情况,常用的方式是加锁(Mutex):

  1. 读与读之间,允许并发。
  2. 读与写,写与写之间互斥。

所以读写锁需要支持以上两种情况。

读写锁

// file: read_write_mutex.h
#pragma once

#include <mutex>
#include <condition_variable>

namespace zone
{
    class read_write_mutex
    {
    public:
        read_write_mutex() = default;
        ~read_write_mutex() = default;

        read_write_mutex(const read_write_mutex &) = delete;
        read_write_mutex & operator=(const read_write_mutex &) = delete;

        read_write_mutex(const read_write_mutex &&) = delete;
        read_write_mutex & operator=(const read_write_mutex &&) = delete;

        void lock_read() {
            std::unique_lock<std::mutex> lock( m_mutex );
            m_cond_read.wait(lock, [this]()-> bool {
                    return m_write_count == 0;
                });
            ++m_read_count;
        }

        void unlock_read() {
            std::unique_lock<std::mutex> lock( m_mutex );
            if (--m_read_count == 0 && m_write_count > 0) {
                m_cond_write.notify_one();
            }
        }

        void lock_write() {
            std::unique_lock<std::mutex> lock( m_mutex );
            ++m_write_count;
            m_cond_write.wait(lock, [this]()-> bool {
                    return m_read_count == 0 && !m_writing;
                });
            m_writing = true;
        }

        void unlock_write() {
            std::unique_lock<std::mutex> lock( m_mutex );
            if (--m_write_count == 0) {
                m_cond_read.notify_all();
            } else {
                m_cond_write.notify_one();
            }
            m_writing = false;
        }

    private:
        volatile size_t m_read_count = 0;
        volatile size_t m_write_count = 0;
        volatile bool m_writing = false;
        std::mutex m_mutex;
        std::condition_variable m_cond_read;
        std::condition_variable m_cond_write;
    };



    template<typename _ReadWriteLock>
    class unique_read_lock
    {
    public:
        explicit unique_read_lock(_ReadWriteLock & rwLock)
            : m_ptr_rw_lock(&rwLock) {
            m_ptr_rw_lock->lock_read();
        }

        ~unique_read_lock() {
            if (m_ptr_rw_lock) {
                m_ptr_rw_lock->unlock_read();
            }
        }

        unique_read_lock() = delete;
        unique_read_lock(const unique_read_lock &) = delete;
        unique_read_lock & operator = (const unique_read_lock &) = delete;
        unique_read_lock(const unique_read_lock &&) = delete;
        unique_read_lock & operator = (const unique_read_lock &&) = delete;

    private:
        _ReadWriteLock * m_ptr_rw_lock = nullptr;
    };



    template<typename _ReadWriteLock>
    class unique_write_lock
    {
    public:
        explicit unique_write_lock(_ReadWriteLock & rwLock)
            : m_ptr_rw_lock(&rwLock) {
            m_ptr_rw_lock->lock_write();
        }

        ~unique_write_lock() {
            if (m_ptr_rw_lock) {
                m_ptr_rw_lock->unlock_write();
            }
        }

        unique_write_lock() = delete;
        unique_write_lock(const unique_write_lock &) = delete;
        unique_write_lock & operator = (const unique_write_lock &) = delete;
        unique_write_lock(const unique_write_lock &&) = delete;
        unique_write_lock & operator = (const unique_write_lock &&) = delete;

    private:
        _ReadWriteLock * m_ptr_rw_lock = nullptr;
    };
}

通过 m_read_count, m_write_countm_writing 记录当前的读写状态(读等待数,写等待数,当前是否正在写入)。

当状态切换时唤醒对应的 m_cond_readm_cond_write 等待,进行读写切换。

接口使用方法:

// 引入读写锁头文件
#include "read_write_mutex.h"

// 定义读写锁
zone::read_write_mutex rwmutex;

void read_function() {
    // 对该读操作,加读锁
    zone::unique_read_lock<zone::read_write_mutex> lock( rwmutex );
    // 读操作
}

void write_function() {
    // 对该写操作,加写锁
    zone::unique_write_lock<zone::read_write_mutex> lock( rwmutex );
    // 写操作
}

测试

以下程序开启输入线程(threadWrite)和打印线程(threadRead)。

输入线程读取 10 次命令行输入后关闭。

打印线程每隔 10 ms 读取关联的 msg 是否有变化,发生变化时打印出来。直到 end == true 时结束。

#include "read_write_mutex.h"
#include <iostream>
#include <thread>
#include <chrono>
#include <string>
#include <future>
#include <atomic>

void readData(zone::read_write_mutex& rwmutex, std::string& msg, int& lastLen) {
    zone::unique_read_lock<zone::read_write_mutex> readLock( rwmutex );
    if (lastLen != msg.length()) {
        lastLen = msg.length();
        std::cout << ">>> [ " << lastLen << " ] " << msg << std::endl;
    }
}

void writeData(zone::read_write_mutex& rwmutex, std::string& msg, std::string in) {
    zone::unique_write_lock<zone::read_write_mutex> writeLock( rwmutex );
    msg += '_' + in;
}

void threadRead(zone::read_write_mutex& rwmutex, std::string& msg, std::atomic<bool>& end) {
    int len = 0;
    while(!end) {
        readData(rwmutex, msg, len);
        std::this_thread::sleep_for(std::chrono::milliseconds(10));
    }
}

void threadWrite() {
    std::string msg = "Hello";
    zone::read_write_mutex rwmutex;
    std::atomic<bool> end(false);
    std::thread th_read(threadRead, std::ref(rwmutex), std::ref(msg), std::ref(end));

    int n = 10;
    for (int i = 0; i < n; ++ i) {
        std::string newMsg;
        std::cout << std::endl << "[ " << i+1 << '/' << n << " ] : ";
        std::cin >> newMsg;
        writeData(rwmutex, msg, newMsg);
    }

    end = true;
    th_read.join();
}

int main() {
    std::thread th_write(threadWrite);
    th_write.join();

    return 0;
}

测试结果:

# g++ -std=c++11 test_rwlock.cpp -lpthread && ./a.out

[ 1/10 ] : >>> [ 5 ] Hello
test

[ 2/10 ] : >>> [ 10 ] Hello_test
zone

[ 3/10 ] : >>> [ 15 ] Hello_test_zone
abc

[ 4/10 ] : >>> [ 19 ] Hello_test_zone_abc
xyz

[ 5/10 ] : >>> [ 23 ] Hello_test_zone_abc_xyz
123

[ 6/10 ] : >>> [ 27 ] Hello_test_zone_abc_xyz_123
AAA

[ 7/10 ] : >>> [ 31 ] Hello_test_zone_abc_xyz_123_AAA
micro

[ 8/10 ] : >>> [ 37 ] Hello_test_zone_abc_xyz_123_AAA_micro
end

[ 9/10 ] : >>> [ 41 ] Hello_test_zone_abc_xyz_123_AAA_micro_end
byb

[ 10/10 ] : >>> [ 45 ] Hello_test_zone_abc_xyz_123_AAA_micro_end_byb
bye
>>> [ 49 ] Hello_test_zone_abc_xyz_123_AAA_micro_end_byb_bye
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!