并发控制

你好 我想知道在3层应用程序中实现并发控制的最佳方法吗? 可能首先想到的是: 客户想要编辑数据集中的记录。 向服务器发送请求,要求锁定该记录 服务器根据锁表接受/拒绝编辑请求 根据这种情况,锁应该引用锁定的记录和使用该记录的客户端。 客户端必须定期向服务器发送保持活动消息。保持活动用于释放锁定的记录,以防我们在编辑操作的混乱中丢失客户端。 我将使用Delphi与datasnap。也许这是一个新手问题,但我不得不问!     
已邀请:
我正在使用jachguate的乐观并发控制回答来回答评论中提出的问题。 我更喜欢在任何地方使用OCC,因为实现更容易。我将讨论使用对象持久性框架的三层应用程序。我的推广计划分为三个级别: 行或对象级别控件,其中每个对象上存储唯一的版本ID。如果您尝试更新对象,则会自动更改版本ID。如果您的版本ID与已存在的版本ID不匹配,则更新失败。 字段或列级锁定。您发送原始对象的完整副本以及更新的副本。更新中的每个字段都会在应用新值之前比较实际值和旧值。可以要求用户解决冲突而不是丢弃冲突,但随着提交中的数据量的增加,这会变得混乱。 悲观锁定。每个对象都有一个锁所有者,通常为null(对象未锁定)。如果要编辑对象,请先锁定它。这里的问题是需要整理锁定,并且围绕它的业务规则可能是丑陋的(需要什么时候超时)。 这样做的好处是大多数时候都采用低成本的OCC路径。对于发生很多但争用率低的事情而言,好处是显着的。想想仓库中的产品跟踪 - 产品一直在移动,但很少同时移动相同的项目,并且当它们解析时很容易(剩余数量=原始数量减去我的移除和移除)。对于(例如)产品重新定位的复杂情况,在产品运输过程中锁定产品可能是有意义的(因为这反映了物理情况)。 当您必须回退到锁定时,能够通知两个用户并拥有通信通道通常很有用。至少在可用时通知想要锁的用户,最好允许他们向锁持有人发送消息,甚至允许他们强制锁定。然后通知失败者“Jo Smith已经锁定你,你失去了你的变化”。让办公室政治排序那个:) 我通常通过用户投诉而不是错误报告来推动后备流程。如果用户抱怨他们在特定流程中经常丢失编辑内容,请进行更改。如果用户抱怨记录过于频繁地被锁定,则必须重构对象映射以增加锁粒度或进行业务流程更改。     
我在设计我的应用程序时考虑了乐观并发控制,当用户想要编辑它时也没有锁定任何记录,也不试图控制并发。 在处理客户端应用的更新时,在设置了适当的内置数据库锁定功能后,在服务器端(应用程序或数据库)完成重要的计算和更新。 DataSnap自动事务回滚可防止这些锁在发生故障时阻止其他并发用户。 使用DataSnap,您可以完全控制在两个用户编辑时使用适用于您的字段的ProviderFlags进行冲突时防止数据丢失。为您要自动检查的任何字段设置pfInWhere,使其在编辑/删除时具有与读取记录时相同的值。 此外,当发生冲突时,您可以在应用程序服务器(提供程序OnUpdateError事件),客户端(TClientDataSet OnReconcileError事件)上以编程方式作出反应,或者甚至要求用户进行适当的冲突解决(请查看New Item存储库中的ReconcileErrorDialog) 。 与此同时,IMHO避免了维护锁定列表,客户端列表,每个客户端列表锁定,保持活动消息,强大的应用程序服务器故障恢复以及所有可能的故障所需的复杂性,您将以更清晰,更好的解决方案结束。     
jachgate给出的方法很好,可能更好,但是如果你想要实现它,你需要在服务启动时创建的服务器上使用
TThreadList
。使用
TThreadList
因为它是线程安全的。每张桌子可以使用
TThreadList
,这样可以最大限度地降低导航列表的性能。 要控制锁定的内容,您需要一个已创建并传递给列表的对象
  TLockedItem = class(TObject)
  public
    iPK: Integer;
    iClientID: Integer;
  end;
要进行实际锁定,你需要这样的东西:
function LockItem(pPK, pClientID: Integer): Boolean;
var
  oLockedItem: TLockedItem;
  oInternalList: TList;
  iCont: Integer;
  bExists: Boolean;
begin
  bExists := False;
  if (Assigned(oLockedList)) then
  begin
    oInternalList := oLockedList.LockList;
    try
      if (oInternalList.Count > 0) then
      begin
        iCont := 0;
        while ((not bExists) and (iCont < oInternalList.Count)) do
        begin
          oLockedItem := TLockedItem(oInternalList[iCont]);
          if (oLockedItem.iPK = pPk) then
            bExists := True
          else
            Inc(iCont);
        end;
      end;
    finally
      oLockedList.UnlockList;
    end;
    if (not bExists) then
    begin
      oLockedItem := TLockedItem.Create;
      oLockedItem.iPK := pPK;
      oLockedItem.iClientID := pClientID;
      oInternalList := oLockedList.LockList;
      try
        oInternalList.Add(oLockedItem);
      finally
        oLockedList.UnlockList;
      end;
    end;
  end;
  Result := bExists;
end;
这只是你需要的一种意识形态。您必须使用类似的逻辑执行解锁方法。您可能需要一个客户端列表,以便在连接丢失的情况下保持每个客户端持有的每个TLockItem的一个点。如果你想要实现这种方法,这不是一个明确的答案,只是推动方向。 祝好运     

要回复问题请先登录注册