如何在"source of truth"具有通用多个操作时同步访问



>我有一个项目存储库,它从视图模型(主线程(和后台服务(后台线程(使用。 两者都可以查询项目存储库和更新项目。

class ItemRepository
{
Item GetItem(int id);
void UpdateItem(Item item);
}
public class Item
{
ItemState State { get; set; }
}
// Component A (view-model)
// Runs on main thread     
void ExecuteUpdateItemCommand()
{
var item = _itemRepo.GetItem(1);
if(item.State == ItemState.First)
{
itemState.State = ItemState.Second;
_itemRepo.UpdateItem(item); // Ideally this should be on a separate thread but for now lets' assume it's on main thread
}
}
// Component B (background service - receives notifications from backedn) 
// Runs on a background thread
void OnNotificationReceived()
{
var item = _itemRepo.GetItem(1);
if(item.State == ItemState.First)
{
item.State = GetNextState(); // some busines logic specific to Component B
_itemRepo.UpdateItem(item);
}
}

我遇到的问题是跨两个线程实现项目查询和更新的同步。

如果不进行同步,item 的状态可能会损坏,因为在 if 语句在一个线程上返回后,另一个线程可能会将 item 状态更新为另一个值,这可能会破坏 item 状态的业务逻辑。

一个简单的解决方案是将组件 A 和组件 B 锁定在同一个共享对象上。从设计的角度来看,如何在组件之间共享此对象(每个组件具有不同的用途(?

我看到的另一种解决方案是拥有一个服务,该服务只是为了同步调用而同时执行组件 A 和组件 B 的业务逻辑,但在我看来这不是一个好的解决方案。

我正在寻找比这些更好的实现。

我的感觉是一个好的解决方案,可能是允许我在上下文上运行事物的解决方案。类似的东西

ItemSyncContext.Run(ExecuteUpdateItemCommand);

ItemSyncContext.Run(OnNotificationReceived);      

理想情况下,您只能调用一次执行这两项更新的服务。如果这不是一个选项,则需要在服务中回答"何时进行更新?如果调用是阻塞的,因此在调用返回之前提交更新,则可以安全地保持代码不变。否则,您需要研究一些机制(即锁定(以确保调用以正确的顺序发生。

如果您的存储库位于数据库(即 .SQL(上,则通过事务序列化访问(具有适当的隔离级别(可能会为同步结束提供另一种方法:

// Component A (view-model)
// Runs on main thread     
void ExecuteUpdateItemCommand()
{
try
{           
_itemRepo.BeginTransaction();
var item = _itemRepo.GetItem(1);
if(item.State == ItemState.First)
{
itemState.State = ItemState.Second;
_itemRepo.UpdateItem(item); // Ideally this should be on a separate thread but for now lets' assume it's on main thread
}
}
catch
{
if (_itemRepo.InTransaction)
{
_itemRepo.RollbackTransaction();
}
throw;
}
finally
{
if (_itemRepo.InTransaction)
{
_itemRepo.CommitTransaction();
}
}
}
// Component B (background service - receives notifications from backedn) 
// Runs on a background thread
void OnNotificationReceived()
{
try
{
_itemRepo.BeginTransaction();
var item = _itemRepo.GetItem(1);
if(item.State == ItemState.First)
{
item.State = GetNextState(); // some busines logic specific to Component B
_itemRepo.UpdateItem(item);
}
}
catch
{
if (_itemRepo.InTransaction)
{
_itemRepo.RollbackTransaction();
}
throw;
}
finally
{
if (_itemRepo.InTransaction)
{
_itemRepo.CommitTransaction();
}
}
}

最新更新