Arhn - архитектура программирования

Как заблокировать объекты с одинаковыми идентификаторами?

У меня есть следующий код:

public void Update(Foo foo)
{
    lock(_locker) 
    {
        UpdateFirstPart(foo.First);
        UpdateSecondPart(foo.Second);
        UpdateThirdPart(foo.Third);
    }
} 

public class Foo 
{
    public int Id;

    public Some1 First;  

    public Some2 Second; 

    public Some3 Third; 
}

Метод Update может выполняться в двух или более потоках, и я использую lock для предотвращения проблем с синхронизацией с foo. Но я хотел бы заблокировать только те Foo, у которых есть похожие Id. Например, если один поток выполняет метод Update с Foo.Id = 1, а другой поток выполняет Update с Foo.Id = 2, то lock не нужен, а если два потока выполняют Update с двумя экземплярами Foo с одним и тем же Id, требуется lock. Можно ли создать такой замок?


  • Переместите _locker в Foo, затем вы сможете lock(foo.Locker). 23.10.2017

Ответы:


1

Вы можете использовать ConcurrentDictionary<TKey, TValue> для хранения замки.

private static readonly ConcurrentDictionary<int, object> ThreadLockDict =
                                                    new ConcurrentDictionary<int, object>();

И используйте его в методе Update:

public static void Update(Foo foo)
{
    // add a new locker object for each foo if it's not already in dictionary
    ThreadLockDict.TryAdd(foo.Id, new object());

    lock (ThreadLockDict[foo.Id])
    {
        // some code
    }
}
23.10.2017
  • Не сильно отличается от моего ;) 23.10.2017
  • Да, идея почти такая же;) Я не видел вашего ответа до того, как опубликовал свой :) Он немного компактнее. 23.10.2017

  • 2

    Вы можете использовать этот класс для получения объекта блокировки для каждого идентификатора:

    public class MultiLockObjects<TKey>
    {
        private readonly ConcurrentDictionary<TKey, Object>  _multiLocker = new ConcurrentDictionary<TKey, Object>();
    
        public Object this[TKey key]
        {
            get
            {
                Object lockObj = _multiLocker.GetOrAdd(key, tKey => new Object());
                return lockObj;
            }
        }
    }
    

    Затем держите его экземпляр в своем классе:

    private MultiLockObjects<int> _idLocks = new MultiLockObjects<int>();
    

    Использование простое:

    public void Update(Foo foo)
    {
        Object idLockObject = _idLocks[foo.Id];
        lock (idLockObject)
        {
            UpdateFirstPart(foo.First);
            UpdateSecondPart(foo.Second);
            UpdateThirdPart(foo.Third);
        }
    }
    
    23.10.2017
  • Не лучше ли использовать GetOrAdd, а не TryGet, за которым следует TryAdd? 23.10.2017
  • @Chris: хороший улов, я думаю, раньше были даже условия гонки. Исправлено 23.10.2017
  • Я не столько уловил это, сколько сравнил с реализацией, которая была у меня в коде. Я также удаляю объект блокировки из словаря в конце заблокированного блока кода, чтобы словарь не слишком раздувался. Я менее уверен, что это можно делать в глобальном масштабе (это в моем конкретном случае). 23.10.2017
  • @Chris: я не вижу твоего кода, ты не ответил, не так ли? Помещение int и пустого объекта в словарь не требует больших затрат. Если у него есть миллионы идентификаторов, он может очистить их сам с помощью _idLocks = new MultiLockObjects<int>(), когда посчитает, что это необходимо. Поскольку Update является методом экземпляра, я предполагаю, что его время жизни ограничено. 23.10.2017
  • Извините, может запутал. Нет, в моем решении есть код, который я использовал. Вы ответили почти так же, поэтому я не стал публиковать свой собственный ответ, а просто предложил поправку к вашему. И вы правы, удаление объектов блокировки после использования вполне могло быть преждевременной оптимизацией с моей стороны... 23.10.2017

  • 3

    Вы можете просто lock(foo) когда будете уверены

    • никакой другой код не использует foo для блокировки
    • ссылочное равенство (идентичность) приемлемо/пригодно для использования, это не использует Id

    Вы не можете заблокировать int Id.


    Часть 2,

    Увы, я использую разные экземпляры Foo с одним и тем же идентификатором.

    Затем вы можете попробовать ConcurrentDictionary<int, object> для хранения другого _locker для каждого идентификатора. Но это добавляет некоторые накладные расходы и проблему очистки этого словаря, когда вы запускаете его в течение длительного времени.

    23.10.2017
  • Увы, я использую разные экземпляры Foo с одинаковыми идентификаторами. 23.10.2017
  • Новые материалы

    Коллекции публикаций по глубокому обучению
    Последние пару месяцев я создавал коллекции последних академических публикаций по различным подполям глубокого обучения в моем блоге https://amundtveit.com - эта публикация дает обзор 25..

    Представляем: Pepita
    Фреймворк JavaScript с открытым исходным кодом Я знаю, что недостатка в фреймворках JavaScript нет. Но я просто не мог остановиться. Я хотел написать что-то сам, со своими собственными..

    Советы по коду Laravel #2
    1-) Найти // You can specify the columns you need // in when you use the find method on a model User::find(‘id’, [‘email’,’name’]); // You can increment or decrement // a field in..

    Работа с временными рядами спутниковых изображений, часть 3 (аналитика данных)
    Анализ временных рядов спутниковых изображений для данных наблюдений за большой Землей (arXiv) Автор: Рольф Симоэс , Жильберто Камара , Жильберто Кейрос , Фелипе Соуза , Педро Р. Андраде ,..

    3 способа решить квадратное уравнение (3-й мой любимый) -
    1. Методом факторизации — 2. Используя квадратичную формулу — 3. Заполнив квадрат — Давайте поймем это, решив это простое уравнение: Мы пытаемся сделать LHS,..

    Создание VR-миров с A-Frame
    Виртуальная реальность (и дополненная реальность) стали главными модными терминами в образовательных технологиях. С недорогими VR-гарнитурами, такими как Google Cardboard , и использованием..

    Демистификация рекурсии
    КОДЕКС Демистификация рекурсии Упрощенная концепция ошеломляющей О чем весь этот шум? Рекурсия, кажется, единственная тема, от которой у каждого начинающего студента-информатика..