атомарное addorupdate (попытка написать именованный шкафчик с использованием параллельного словаря)

отмечает, что AddOrUpdate не является атомарным (и не может гарантировать, что делегаты не будут запускаться более одного раза).

Я пытаюсь реализовать реализацию блокировки имен, используя параллельный словарь a la здесь, но там, где словарь не должен расти вечно, например:

public class ConcurrentDictionaryNamedLocker : INamedLocker
{
    // the IntObject values serve as the locks and the counter for how many RunWithLock jobs 
    // are about to enter or have entered the critical section.
    private readonly ConcurrentDictionary<string, IntObject> _lockDict = new ConcurrentDictionary<string, IntObject>();
    private static readonly IntObject One = new IntObject(1);
    private readonly Func<string, IntObject, IntObject> _decrementFunc = (s, o) => o - 1;
    private readonly Func<string, IntObject, IntObject> _incrementFunc = (s, o) => o + 1;
    private readonly Func<string, IntObject> _oneFunc = s => new IntObject(1);
    private readonly Func<string, IntObject> _zeroFunc = s => new IntObject(0);

    public TResult RunWithLock<TResult>(string name, Func<TResult> body)
    {
        name = name.ToLower();
        TResult toReturn;
        lock (_lockDict.AddOrUpdate(name, _oneFunc, _incrementFunc))
        {
            toReturn = body();
            if (!_lockDict.TryRemove(name, One))
                _lockDict.AddOrUpdate(name, _zeroFunc, _decrementFunc);
        }
        return toReturn;
    }

    public void RunWithLock(string name, Action body)
    {
        name = name.ToLower();
        lock (_lockDict.AddOrUpdate(name, _oneFunc, _incrementFunc))
        {
            body();
            if (!_lockDict.TryRemove(name, One))
                _lockDict.AddOrUpdate(name, _zeroFunc, _decrementFunc);
        }
    }
}

Но проблема в том, что AddOrUpdate не является атомарным, поэтому я вижу, что часто записи не удаляются при возникновении разногласий. Однако я почти уверен, что если бы AddOrUpdate был атомарным, приведенный выше код выполнил бы свою работу, и записи были бы удалены соответствующим образом.

Обратите внимание на использование условного удаления методом расширения key + val TryRemove (key, val), упомянутого в здесь. Кроме того, IntObject - это простая изменяемая объектная оболочка типа int.

Какие у меня варианты? Существуют ли какие-либо параллельные реализации словарей, которые имеют 1. атомарное условное (по ключу и значению) удаление и 2. AddOrUpdate является атомарным и гарантирует, что делегаты не запускаются более одного раза?

Есть еще идеи? Я бы хотел, чтобы именованный шкафчик был быстрым, но не имел проблем с нехваткой памяти, учитывая неограниченное пространство имен блокировки, но с небольшим количеством конфликтов по данному имени. Насколько мне известно, блокировка интернирования строк по имени постоянно растет, никогда не очищается и имеет другие побочные эффекты. А мьютексы полумедленны и имеют различные неприятности (ограничение в 260 символов).


person TravisK    schedule 25.06.2013    source источник
comment
Вы могли бы использовать словарь, набранный как ConcurrentDictionary<TKey, Lazy<TValue>>, как я описал в этом ответе stackoverflow.com/a/12611341/1236734   -  person JG in SD    schedule 19.05.2016


Ответы (1)


Было бы полезно увидеть точный код для IntValue, поскольку этот код выполняется в делегате AddOrUpdate.

Я думаю, проблема в том, что код ожидает, что IntValue экземпляра будут обоими:

  • Изменяемый, чтобы заблокировать единственный экземпляр IntValue, связанный с каждой строкой (несмотря на то, что счетчик ссылок увеличивается позже)
  • Неизменяемый, поэтому сравнение IntValues со статическим One работает как критерий удаления

Если я изменю код так, чтобы IntValue поддерживал неизменный ноль, похоже, это сработает:

private readonly Func<string, IntObject> _zeroFunc = s => IntObject.Zero;

...

public void RunWithLock(string name, Action body)
{
    name = name.ToLower();
    lock (_lockDict.AddOrUpdate(name, _oneFunc, _incrementFunc))
    {
        body();
        _lockDict.AddOrUpdate(name, _zeroFunc, _decrementFunc);
        _lockDict.TryRemove(name, IntObject.Zero);
    }
}

Но я решил реализовать IntValue методы, подобные этому, и как неоператорные:

    internal IntObject Dec(int p)
    {
        var newVal = Interlocked.Decrement(ref value);
        if (newVal == 0) return Zero;
        return this;
    }
person Peter Wishart    schedule 31.12.2014