来自多个线程的共享锁可能会使寻找独占锁的单个线程饿死
Shared locks from multiple threads can starve a single thread looking for an exclusive lock
假设我有几个线程从共享内存中读取数据。所有这些线程都重复获取共享互斥体上的共享锁以访问此内存。
假设我有一个写入此共享内存的线程,它获取了独占锁。
如果有足够多的读线程,可能永远不会有一个写线程能够获取独占锁的时间点,因为共享锁在任何时间点都被多个线程持续持有。
解决这个问题最简单直接的模式是什么?我正在寻找一种简单直接的方法来解决这个问题,最好是使用 STL。
C++ 中的示例实现也会有所帮助。
我的首选方案是CsLibGuarded,您可以选择费用。
我已经使用了lr_guarded,这样写入修改一个副本,而读取在另一侧继续,然后当写入完成时,所有新的读取都会转到修改后的一侧,依此类推。在所有读者离开后,写入也可以修改另一侧。
(未经测试的代码)
using MapType = std::map<std::string, std::shared_ptr<ComplicatedObject>>;
lr_guarded<MapType> map;
void MyCache::insert(std::string key, std::shared_ptr<ComplicatedObject> element) {
m_cache.modify(
[&key, &element]
(MapType & map) {
map.emplace(key, element);
});
}
这应该有效。
它并不复杂,如果您有多个作者,则不提供任何订单保证。但它应该有效。
#include <condition_variable>
#include <mutex>
class RWLock
{
std::mutex lock;
std::condition_variable writeWaiters;
std::condition_variable readWaiters;
int waitingWriters;
int currentWriters;
int currentReaders;
public:
RWLock()
: waitingWriters(0)
, currentWriters(0)
, currentReaders(0)
{}
private:
friend class RGuard;
void readLock()
{
std::unique_lock<std::mutex> guard(lock);
readWaiters.wait(guard, [&]{return waitingWriters == 0;});
++currentReaders;
}
void readUnLock()
{
std::unique_lock<std::mutex> guard(lock);
--currentReaders;
if (currentReaders == 0) {
writeWaiters.notify_one();
}
}
private:
friend class WGuard;
void writeLock()
{
std::unique_lock<std::mutex> guard(lock);
++waitingWriters;
writeWaiters.wait(guard, [&]{return (currentReaders != 0 || currentWriters != 0);});
++currentWriters;
}
void writeUnLock()
{
std::unique_lock<std::mutex> guard(lock);
--waitingWriters;
--currentWriters;
if (waitingWriters != 0) {
writeWaiters.notify_one();
}
else {
readWaiters.notify_all();
}
}
}
class RGuard
{
RWLock& lock;
public:
RGuard(RWLock& lock)
: lock(lock)
{
lock.readLock();
}
~RGuard()
{
lock.readUnLock();
}
};
class WGuard
{
RWLock& lock;
public:
WGuard(RWLock& lock)
: lock(lock)
{
lock.writeLock();
}
~WGuard()
{
lock.writeUnLock();
}
};
int main()
{
RWLock lock;
RGuard guardR(lock);
WGuard guardW(lock);
}
假设我有几个线程从共享内存中读取数据。所有这些线程都重复获取共享互斥体上的共享锁以访问此内存。
假设我有一个写入此共享内存的线程,它获取了独占锁。
如果有足够多的读线程,可能永远不会有一个写线程能够获取独占锁的时间点,因为共享锁在任何时间点都被多个线程持续持有。
解决这个问题最简单直接的模式是什么?我正在寻找一种简单直接的方法来解决这个问题,最好是使用 STL。
C++ 中的示例实现也会有所帮助。
我的首选方案是CsLibGuarded,您可以选择费用。
我已经使用了lr_guarded,这样写入修改一个副本,而读取在另一侧继续,然后当写入完成时,所有新的读取都会转到修改后的一侧,依此类推。在所有读者离开后,写入也可以修改另一侧。
(未经测试的代码)
using MapType = std::map<std::string, std::shared_ptr<ComplicatedObject>>;
lr_guarded<MapType> map;
void MyCache::insert(std::string key, std::shared_ptr<ComplicatedObject> element) {
m_cache.modify(
[&key, &element]
(MapType & map) {
map.emplace(key, element);
});
}
这应该有效。
它并不复杂,如果您有多个作者,则不提供任何订单保证。但它应该有效。
#include <condition_variable>
#include <mutex>
class RWLock
{
std::mutex lock;
std::condition_variable writeWaiters;
std::condition_variable readWaiters;
int waitingWriters;
int currentWriters;
int currentReaders;
public:
RWLock()
: waitingWriters(0)
, currentWriters(0)
, currentReaders(0)
{}
private:
friend class RGuard;
void readLock()
{
std::unique_lock<std::mutex> guard(lock);
readWaiters.wait(guard, [&]{return waitingWriters == 0;});
++currentReaders;
}
void readUnLock()
{
std::unique_lock<std::mutex> guard(lock);
--currentReaders;
if (currentReaders == 0) {
writeWaiters.notify_one();
}
}
private:
friend class WGuard;
void writeLock()
{
std::unique_lock<std::mutex> guard(lock);
++waitingWriters;
writeWaiters.wait(guard, [&]{return (currentReaders != 0 || currentWriters != 0);});
++currentWriters;
}
void writeUnLock()
{
std::unique_lock<std::mutex> guard(lock);
--waitingWriters;
--currentWriters;
if (waitingWriters != 0) {
writeWaiters.notify_one();
}
else {
readWaiters.notify_all();
}
}
}
class RGuard
{
RWLock& lock;
public:
RGuard(RWLock& lock)
: lock(lock)
{
lock.readLock();
}
~RGuard()
{
lock.readUnLock();
}
};
class WGuard
{
RWLock& lock;
public:
WGuard(RWLock& lock)
: lock(lock)
{
lock.writeLock();
}
~WGuard()
{
lock.writeUnLock();
}
};
int main()
{
RWLock lock;
RGuard guardR(lock);
WGuard guardW(lock);
}