gpt4 book ai didi

c++ - 您将如何在 C++11 中实现自己的读/写锁?

转载 作者:IT老高 更新时间:2023-10-28 12:32:15 65 4
gpt4 key购买 nike

我有一组数据结构需要用读/写锁来保护。我知道 boost::shared_lock,但我想有一个使用 std::mutex、std::condition_variable 和/或 std::atomic 的自定义实现,以便我可以更好地理解它是如何工作的(稍后再调整它) .

每个数据结构(可移动,但不可复制)都将继承自一个名为 Commons 的类,该类封装了锁定。我希望公共(public)界面看起来像这样:

class Commons {
public:
void read_lock();
bool try_read_lock();
void read_unlock();

void write_lock();
bool try_write_lock();
void write_unlock();
};

...这样它就可以被某些人公开继承:

class DataStructure : public Commons {};

我正在编写科学代码,通常可以避免数据竞争;这个锁主要是为了防止我以后可能会犯的错误。因此,我的优先级是低读取开销,因此我不会过多地妨碍正确运行的程序。每个线程可能会在自己的 CPU 内核上运行。

能否请您给我看(伪代码可以)读/写锁?我现在拥有的应该是防止作家饥饿的变体。到目前为止,我的主要问题是 read_lock 在检查读取是否安全与实际增加读取器计数之间存在差距,之后 write_lock 知道要等待。

void Commons::write_lock() {
write_mutex.lock();
reading_mode.store(false);
while(readers.load() > 0) {}
}

void Commons::try_read_lock() {
if(reading_mode.load()) {
//if another thread calls write_lock here, bad things can happen
++readers;
return true;
} else return false;
}

我对多线程有点陌生,我真的很想了解它。提前感谢您的帮助!

最佳答案

这是使用互斥锁和条件变量的简单读/写锁的伪代码。互斥 API 应该是不言自明的。假设条件变量有一个成员 wait(Mutex&) (原子地!)丢弃互斥锁并等待条件发出信号。该条件由唤醒一个服务员的signal()或唤醒所有服务员的signal_all()发出信号。

read_lock() {
mutex.lock();
while (writer)
unlocked.wait(mutex);
readers++;
mutex.unlock();
}

read_unlock() {
mutex.lock();
readers--;
if (readers == 0)
unlocked.signal_all();
mutex.unlock();
}

write_lock() {
mutex.lock();
while (writer || (readers > 0))
unlocked.wait(mutex);
writer = true;
mutex.unlock();
}

write_unlock() {
mutex.lock();
writer = false;
unlocked.signal_all();
mutex.unlock();
}

不过,这种实现有很多缺点。

当锁可用时唤醒所有服务员

如果大多数等待者都在等待写锁,这是浪费 - 毕竟大多数等待者将无法获取锁,并继续等待。简单地使用 signal() 是行不通的,因为您确实想唤醒所有等待读取锁解锁的人。因此,要解决这个问题,您需要单独的条件变量来实现可读性和可写性。

不公平。读者饿死作者

您可以通过跟踪挂起的读写锁的数量来解决这个问题,或者在出现挂起的写锁时停止获取读锁(尽管这样您会饿死读者!),或者随机唤醒所有读者或一个writer(假设您使用单独的条件变量,请参阅上面的部分)。

锁没有按照请求的顺序处理

为了保证这一点,您需要一个真正的等待队列。你可以例如为每个等待者创建一个条件变量,并在释放锁后向队列头部的所有读取器或单个写入器发出信号。

即使是纯读取工作负载也会因互斥锁而引起争用

这个很难修复。一种方法是使用原子指令来获取读或写锁(通常是比较和交换)。如果由于锁定而导致获取失败,您将不得不退回到互斥锁。但是,正确地做到这一点非常困难。此外,仍然存在争用 - 原子指令远非免费,尤其是在具有大量内核的机器上。

结论

正确实现同步原语困难。实现高效且公平的同步原语甚至 更难。它几乎没有返回。 Linux 上的 pthreads,例如包含一个读写器锁,它使用 futexes 和原子指令的组合,因此它的性能可能优于你在几天的工作中所能想到的任何东西。

关于c++ - 您将如何在 C++11 中实现自己的读/写锁?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12033188/

65 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com