Условная синхронизация, блокировка - PullRequest
0 голосов
/ 19 сентября 2018

У меня есть вопрос о том, как реализовать список / словарь так, чтобы он не блокировался во время чтения и блокировался во время записи.Пример моего кода из проекта GitHub: TextProcessorsCache.cs

/// <summary>
/// Cache of text processors that can be re-used as they do not have
/// state
/// </summary>
public static class TextProcessorsCache
{
    private static readonly ConcurrentDictionary<Type, ITextProcessor> cache;

    static TextProcessorsCache()
    {
        cache = new ConcurrentDictionary<Type, ITextProcessor>();
    }

    public static ITextProcessor GetInstance<T>() where T: class, ITextProcessor, new()
    {
        return cache.GetOrAdd(typeof(T), new T());
    }
}

Я хочу избавиться от поля ConcurrentDictionary<Type,ITextProcessor>, вместо этого я хочу, чтобы любые вызовы для кэширования проверяли наличие значенияпо ключу, но для записи он заблокирует и приостановит все чтения, пока значение не будет записано в словарь

Impl с ReaderWriterLock.

public static class TextProcessorsCache 
{
private static readonly Dictionary<Type, ITextProcessor> cache;
private static ReaderWriterLockSlim locker = new ReaderWriterLockSlim();

static TextProcessorsCache() 
{
  cache = new Dictionary<Type, ITextProcessor>();
}

 public static ITextProcessor GetInstance<T>() where T: class, ITextProcessor, new()
 {
     ITextProcessor processor;
     locker.EnterReadLock();

         cache.TryGetValue(typeof(T), out processor);

     locker.ExitReadLock();

         if (processor == null) 
         {  
              locker.EnterWriteLock();

              if (cache.TryGetValue(typeof(T), out processor))
              {
                   locker.ExitWriteLock();
                   return processor;
              }

              processor = new T();
              cache[typeof(T)] = processor;

              locker.ExitWriteLock();                 
         }
     return processor;
 }   
}

1 Ответ

0 голосов
/ 19 сентября 2018

Один из возможных подходов состоит в том, чтобы использовать дженерики, чтобы гарантировать, что только один экземпляр каждой реализации когда-либо будет создан, как показано ниже. Класс Cache гарантирует, что будет создан только один из каждого T (типа) (для всех вызовов Get для этого T).

Вам нужно будет профилировать, чтобы подтвердить, что эта реализация быстрее / медленнее, чем ваша текущая реализация.

using System;

namespace Sample
{
    public class Runner
    {
        public static void Main()
        {
            TextProcessorsCache.GetInstance<TextProcessor1>();
            TextProcessorsCache.GetInstance<TextProcessor2>();
            TextProcessorsCache.GetInstance<TextProcessor1>(); // this should not write to the console
            TextProcessorsCache.GetInstance<TextProcessor2>(); // this should not write to the console

            Console.ReadLine();
        }
    }

    public static class TextProcessorsCache
    {
        public static ITextProcessor GetInstance<T>() where T : class, ITextProcessor, new()
        {
            return Cache<T>.Get();
        }
    }

    static class Cache<T> where T : class, ITextProcessor, new()
    {
        internal static T value;
        static Cache()
        {
            value = new T();
        }

        public static T Get()
        {
            return value;
        }
    }

    public interface ITextProcessor
    {
    }

    public class TextProcessor1 : ITextProcessor
    {
        public TextProcessor1()
        {
            Console.WriteLine("1 constructed");
        }
    }

    public class TextProcessor2 : ITextProcessor
    {
        public TextProcessor2()
        {
            Console.WriteLine("2 constructed");
        }
    }
}
...