Как синхронизировать доступ, когда «источник правды» имеет общие множественные операции - PullRequest
0 голосов
/ 15 мая 2018

У меня есть хранилище элементов, которое используется как для view-модели (основной поток), так и для фонового сервиса (фоновый поток). Оба могут запрашивать хранилище элементов и обновлять элементы.

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

Проблема, с которой я столкнулся, заключается в реализации синхронизации запроса элемента и обновлении в двух потоках.

Без синхронизации состояние элемента может стать поврежденным, поскольку после возврата оператора if в одном потоке другой поток может обновлять состояние элемента на другое значение, что может нарушить бизнес-логику состояния элемента.

Простым решением является блокировка компонента A и компонента B на одном и том же общем объекте. С точки зрения дизайна, как разделить этот объект между компонентами (каждый со своей целью)?

Другое решение, которое я вижу, состоит в том, чтобы иметь службу, которая выполняет и бизнес-логику компонента A и компонента B только для синхронизации вызовов, но, на мой взгляд, это не очень хорошее решение.

Я ищу лучшие реализации, чем эти.

Мне кажется, хорошее решение может быть таким, которое позволяет мне управлять вещами в контексте. Что-то вроде

   ItemSyncContext.Run(ExecuteUpdateItemCommand);

и

   ItemSyncContext.Run(OnNotificationReceived);      

Ответы [ 2 ]

0 голосов
/ 15 мая 2018

Если ваш репозиторий находится над базой данных (т. Е. 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();
        }
    }
}
0 голосов
/ 15 мая 2018

В идеале у вас будет только один звонок в службу, которая выполняет оба обновления.Если это не вариант, вам нужно ответить на вопрос "когда происходит обновление?"в сервисе.если вызов блокируется, поэтому обновленное происходит до его возврата, вы можете безопасно сохранить свой код как есть.В противном случае вам нужно изучить какой-то механизм (т.е. блокировку), чтобы убедиться, что вызовы происходят в правильном порядке.

...