如何实现脱机读写器锁定

时间:2009-02-11 19:43:38

标签: c# database algorithm domain-driven-design

问题的某些背景

  • 此问题中的所有对象都是持久的。
  • 所有请求都来自Silverlight客户端,通过二进制协议(Hessian)而不是WCF与应用服务器通信。
  • 每个用户都有一个会话密钥(不是ASP.NET会话),它将是一个字符串,整数或GUID(到目前为止尚未确定)。

某些对象可能需要很长时间才能编辑(30分钟或更长时间),因此我们决定使用悲观的离线锁定。悲观,因为必须协调冲突对用户来说太烦人了,离线因为客户端没有永久连接到服务器。

我没有将会话/对象锁定信息存储在对象本身中,而是决定任何可能锁定其实例的聚合根应该实现一个接口ILockable

public interface ILockable
{
  Guid LockID { get; }
}

此LockID将是“Lock”对象的标识,该对象包含哪个会话锁定它的信息。

现在,如果这是简单的悲观锁定,我将能够非常简单地实现这一点(使用Lock上的递增版本号来识别更新冲突),但我真正需要的是ReaderWriter悲观的离线锁定。

原因是应用程序的某些部分将执行读取这些复杂结构的操作。这些包括

之类的内容
  • 阅读单个结构以克隆它。
  • 读取多个结构以创建二进制文件以将数据“发布”到外部源。

读取锁定将保持很短的时间,通常不到一秒钟,但在某些情况下,它们可以在猜测时保持约5秒钟。

写锁定大部分时间都会被保留,因为它们主要由人类持有。

两个用户同时尝试编辑同一聚合的概率很高,很多用户同时也需要暂时读取锁定的概率很高。我正在寻找关于如何实现这一点的建议。

要做的另一点是,如果我想放置一个写锁并且有一些读锁,我想“排队”写锁,这样就不会放置新的读锁。如果读取锁定在X秒内被移除,则获得写入锁定,如果没有,则写入锁定退出;写入锁定排队时不会放置新的读锁定。

到目前为止,我有这个想法

  1. Lock对象将具有版本号(int),因此我可以检测多次更新冲突,重新加载,再试一次。
  2. 它将有一个用于读锁的字符串[]
  3. 用于保存具有写锁定的会话ID的字符串
  4. 用于保存排队写锁定的字符串
  5. 可能是一个递归计数器,允许同一个会话多次锁定(对于读取和写入锁定),但还不确定。
  6. 规则:

    • 如果存在写锁定或排队写锁定,则无法发出读锁定。
    • 如果存在写锁定或排队写锁定,则无法写入锁定。
    • 如果根本没有锁,则可以放置写锁。
    • 如果有读锁定,则写入锁定将排队,而不是放置完全写入锁定。 (如果在X时间之后读取锁定没有消失,则锁定会退回,否则会升级)。
    • 无法为具有读锁定的会话排队写入锁。

    任何人都可以看到任何问题吗?建议替代方案?什么?在决定采取何种方法之前,我会很感激反馈。

3 个答案:

答案 0 :(得分:1)

您是否考虑过mvcc实施:

当然,它实现起来会更难,但似乎比你解释的要多得多。把想法扔到那里。

答案 1 :(得分:1)

(编辑为更多的答案,而不是一堆问题)

我也在Yahoo DDD列表中回复了这个问题,但也可以在这里完整性。

你没有明确提到(我可以看到)任何类型的写锁定超时。我知道他们将能够被关押很长时间,但是如果出现某种超时(即使它附带通知),这将是一种谨慎的策略。

除非(或者甚至更好),您可能需要考虑使用一种方法来手动释放锁,如果您还没有考虑过 - 取决于系统的用户/角色,可能某种易于使用的管理界面。

另外,你也毫不怀疑这一点,确保读锁数组是线程安全的。

答案 2 :(得分:0)

如果你真的想要推出自己的锁定,我会遵循类似于线程锁定的锁定模式,例如在ReaderWriterLockSlim中。

http://msdn.microsoft.com/en-us/library/system.threading.readerwriterlockslim.aspx