[英]Cache locking pattern with C++
我想知道是否有比简单锁更好的缓存锁定方案:
Mutex lock;
get(key) {
LockGuard(lock);
if (cache.has(key)) {
return cache[key];
} else {
data = remoteclient.getslow();
cache[key] = data;
return data;
}
}
假设您有很多相同的请求,那么您每次都会序列化对get()的访问。 可以用ReadWriter锁来做一些更聪明的事情吗?
即,如果您做类似的事情:
ReadersWritersLock lock;
get(key) {
{
ReadLockGuard(lock);
if (cache.has(key)) {
return cache[key];
}
}
WriteLockGuard(lock);
data = remoteclient.getslow();
cache[key] = data;
return data;
}
}
现在,如果发生缓存命中,这将允许多个用户同时获取get()。 但是,如果两个用户大约在同一时间到达第一个get(),则他们可能都将尝试进入代码的第二部分来获取数据。 这似乎是个好主意吗?
还有其他想法可以优化这种代码吗?
我对发布的代码不满意的一件事是在两个代码段中
remoteclient.getslow();
在缓存被锁定时调用。 如果remoteclient.getslow()实际上可能需要很长时间才能返回(顾名思义),则任何其他尝试访问缓存的线程最终都会被阻塞很长时间(即,直到getslow()返回并且即使他们只对缓存中已经存在的无关数据感兴趣,调用它的线程也会释放锁!
为了避免这种情况,我会改为在LockGuard范围之外(即,在解锁缓存时)调用remoteclient.getslow()。 然后,在remoteclient.getslow()返回结果之后,我将重新锁定缓存并使用检索到的值更新缓存。 这样,缓存就不会长时间锁定。
(当然,这样做会打开多个线程为同一数据项调用remoteclient.getslow()的可能性,如果它们都决定在大约同一时间都需要相同的数据……但这是可以接受的。否则,您可以设计一种机制来指示正在检索特定的高速缓存值,并使其他线程处于阻塞状态,直到检索完成为止……如果那对您来说值得额外的复杂性。那可能需要条件变量之类的东西才能正确执行)
您的伪代码有正确的主意,但是有竞争条件。
一旦ReadLockGuard超出范围,您就会丢失锁,这意味着在WriteLockGuard有时间获取锁之前,可以由另一个线程修改数据结构。
如果您的读取器/写入器锁实现支持可升级的锁,则应使用该锁。 否则,在获取写锁之后,您需要仔细检查缓存,以防缓存填充在“读取器”发行版和“写入器”发行版之间。
两个线程可能会进入get()的“写入”部分,但可能性很小。 如果您担心额外的getslow()调用的代价,则可以在writer锁内再次检查。
ReadersWritersLock lock;
get(key) {
{
ReadLockGuard(lock);
if (cache.has(key)) {
return cache[key];
}
}
WriteLockGuard(lock);
if (cache.has(key) == false) {
data = remoteclient.getslow();
cache[key] = data;
return data;
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.