C++中的线程安全读写锁实现

多线程编程中,确保数据的一致性和完整性是非常重要的。为了实现这一点,通常需要使用锁来控制对共享资源的访问。读写锁是一种允许多个线程同时读取共享资源,但只允许一个线程写入的锁。本文将介绍如何在C++中实现线程安全的读写锁,包括自定义的RWMutex类和C++17中的std::shared_mutex。

RWMutex的实现

RWMutex是一种读写锁的实现,它允许多个线程同时读取共享资源,但只允许一个线程写入。这种锁的实现基于以下原理:

  • 当线程需要读取共享资源时,它会尝试获取读锁。如果读锁已经被其他线程持有,那么它会等待直到读锁被释放。
  • 当线程需要写入共享资源时,它会尝试获取写锁。如果写锁已经被其他线程持有,那么它会等待直到写锁被释放。
  • 如果线程已经持有读锁,那么它可以继续读取共享资源,而不需要再次获取读锁。
  • 如果线程已经持有写锁,那么它可以继续写入共享资源,而不需要再次获取写锁。

RWMutex的实现包括以下关键组件:

  • 一个互斥锁(mutex),用于保护共享资源。
  • 一个计数器,用于跟踪当前持有读锁的线程数量。
  • 一个条件变量,用于协调线程之间的锁请求。

以下是一个简单的RWMutex实现示例:

class RWMutex { private: std::mutex mutex; int readCount; std::condition_variable cond; public: void lockRead() { std::unique_lock lock(mutex); cond.wait(lock, [this] { return writeCount == 0; }); readCount++; } void unlockRead() { std::unique_lock lock(mutex); readCount--; if (readCount == 0) { cond.notify_all(); } } void lockWrite() { std::unique_lock lock(mutex); cond.wait(lock, [this] { return readCount == 0 && writeCount == 0; }); writeCount++; } void unlockWrite() { std::unique_lock lock(mutex); writeCount--; cond.notify_all(); } };

std::shared_mutex的使用

C++17开始,标准库提供了std::shared_mutex,它提供了一种更简洁和高效的方式来实现读写锁。std::shared_mutex允许多个线程同时读取共享资源,但只允许一个线程写入。

std::shared_mutex的使用非常简单,以下是一个示例:

std::shared_mutex mutex; void readerFunction() { std::shared_lock lock(mutex); // 读取共享资源 } void writerFunction() { std::unique_lock lock(mutex); // 写入共享资源 }

tlock类的实现

tlock类是一个模板类,它封装了RWMutex或std::shared_mutex,提供了一种更高级的接口来实现线程安全的读写锁。tlock类的主要特点包括:

  • 支持升级和降级锁。
  • 支持直接访问共享资源。
  • 提供了readlock、writelock和rwlock等便捷方法。

以下是一个tlock类的实现示例:

template<typename T> class tlock { private: mutable T t; mutable RWMutex m; public: tlock(Args... args) : t(args...) {} proxy r() const { return proxy(&t, &m, 1); } proxy w() { return proxy(&t, &m, 2); } void readlock(std::function<void(const T&)> f) const { proxy mx(&t, &m, 1); f(*mx.getp()); } void writelock(std::function<void(T&)> f) { proxy mx(&t, &m, 2); f(*mx.getp()); } proxy operator->() { return w(); } const proxy operator->() const { return r(); } };

错误用法示例

在没有使用tlock的情况下,直接在多线程环境中访问共享资源可能会导致数据不一致。以下是一个错误用法示例:

std::vector<int> s; std::thread t1([&]() { s.push_back(0); }); std::thread t2([&]() { s.push_back(1); }); std::thread t3([&]() { s.push_back(2); }); std::thread t4([&]() { s.push_back(3); }); std::thread t5([&]() { s.push_back(4); }); t1.join(); t2.join(); t3.join(); t4.join(); t5.join();

在这个示例中,多个线程同时向s中添加元素,可能会导致数据不一致。

正确用法示例

使用tlock类可以确保在多线程环境中安全地访问共享资源。以下是一个正确用法示例:

tlock<std::vector<int>> s; std::thread t1([&]() { s->push_back(0); }); std::thread t2([&]() { s->push_back(1); }); std::thread t3([&]() { s->push_back(2); }); std::thread t4([&]() { s->push_back(3); }); std::thread t5([&]() { s->push_back(4); }); t1.join(); t2.join(); t3.join(); t4.join(); t5.join();
沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485