Looking for .net Keywords? Try Ask4Keywords

.NET Framework ConcurrentDictionary, дополненный Lazy'1, уменьшает дублирование вычислений


пример

проблема

ConcurrentDictionary сияет, когда дело доходит до моментального возврата существующих ключей из кеша, в основном блокировки и борьбы на гранулированном уровне. Но что, если создание объекта действительно дорого, перевешивает стоимость переключения контекста, и некоторые промахи промахов происходят?

Если один и тот же ключ запрашивается из нескольких потоков, один из объектов, возникающих в результате встречных операций, в конечном итоге будет добавлен в коллекцию, а остальные будут выброшены, теряя ресурсы ЦП для создания ресурса объекта и памяти для временного хранения объекта , Другие ресурсы также могут быть потрачены впустую. Это действительно плохо.

Решение

Мы можем комбинировать ConcurrentDictionary<TKey, TValue> с Lazy<TValue> . Идея заключается в том, что метод ConcurrentDictionary GetOrAdd может вернуть значение, фактически добавленное в коллекцию. В этом случае теряются и потери Lazy-объектов, но это не так уж сложно, так как сам Lazy-объект относительно невысок. Свойство Value проигрывающего Lazy никогда не запрашивается, потому что мы умны, чтобы запрашивать только свойство Value того, которое фактически добавлено в коллекцию, - тот, который возвращается из метода GetOrAdd:

public static class ConcurrentDictionaryExtensions
{
    public static TValue GetOrCreateLazy<TKey, TValue>(
        this ConcurrentDictionary<TKey, Lazy<TValue>> d,
        TKey key,
        Func<TKey, TValue> factory)
    {
        return
            d.GetOrAdd(
                key,
                key1 =>
                    new Lazy<TValue>(() => factory(key1),
                    LazyThreadSafetyMode.ExecutionAndPublication)).Value;
    }
}

Кэширование объектов XmlSerializer может быть особенно дорогостоящим, и в старте приложения также много споров. И есть еще кое-что: если это пользовательские сериализаторы, утечка памяти также будет продолжаться до конца жизненного цикла процесса. Единственное преимущество ConcurrentDictionary в этом случае заключается в том, что для остальной части жизненного цикла процесса не будет блокировок, но запуск приложения и использование памяти будут неприемлемыми. Это работа для нашего ConcurrentDictionary, дополненная Lazy:

private ConcurrentDictionary<Type, Lazy<XmlSerializer>> _serializers =
    new ConcurrentDictionary<Type, Lazy<XmlSerializer>>();

public XmlSerializer GetSerialier(Type t)
{
    return _serializers.GetOrCreateLazy(t, BuildSerializer);
}

private XmlSerializer BuildSerializer(Type t)
{
    throw new NotImplementedException("and this is a homework");
}