使用C ++缓存锁定模式

时间:2011-10-14 22:43:24

标签: c++ multithreading locking

我想知道缓存的锁定方案是否比简单的锁更好:

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(),他们可能会尝试进入代码的第二部分来获取数据。这看起来是个好主意吗?

优化此类代码的其他任何想法?

3 个答案:

答案 0 :(得分:2)

我不喜欢发布的代码的一件事是,在两个片段中,调用

remoteclient.getslow();
缓存被锁定时调用

。如果remoteclient.getslow()实际上可能需要很长时间才能返回(如名称所示),那么任何其他尝试访问缓存的线程最终都会被阻塞(即直到getslow()返回并且正在调用它的线程释放锁定... ...即使他们只对缓存中已存在的无关数据感兴趣!

为了避免这种情况,我会在LockGuard的范围之外调用remoteclient.getslow()(即在缓存解锁时)。然后,在remoteclient.getslow()返回结果之后,我将重新锁定缓存并使用检索到的值更新缓存。这样,缓存永远不会被锁定。

(当然这样做会打开多个线程为同一个数据项调用remoteclient.getslow()的可能性,如果他们都决定在大约同一时间需要相同的数据......但是是一个可接受的副作用。或者如果没有,你可以设计一种机制来指示特定的缓存值正在被检索的过程中,让其他线程阻塞直到检索完成...如果值得额外的复杂性对你来说。这可能需要条件变量等来正确完成)

答案 1 :(得分:1)

您的伪代码有正确的想法,但它有竞争条件。

只要ReadLockGuard超出范围,就会失去锁定,这意味着在WriteLockGuard有时间获取锁定之前,另一个线程可以修改数据结构。

如果您的读者/作者锁实现支持可升级锁,那么您应该使用它。否则,在获取写入锁之后,您需要仔细检查缓存,以防它在“reader”版本和“writer”获取之间填充。

答案 2 :(得分:1)

两个线程可能会进入get()的'writing'部分,但可能不太可能。如果您担心额外的getslow()调用的惩罚,您可以再次检查编写器锁。

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;
  }
 }