Проблемы с использованием статического MemcachedClient

#c# #asp.net-mvc #memcached

#c# #asp.net-mvc #memcached

Вопрос:

Я использую Memcached для хранения данных для быстрого доступа. Я читал, что создание MemcachedClient является дорогостоящим, и видел использование MemcachedClient как статического (см.: Ссылка)

Итак, я использовал шаблон Singleton для своего клиента как :

 public class CommonObjectsCache
{
    private static CommonObjectsCache _cache;
    private static MemcachedClient _client;

    public static MemcachedClient Client
    {
        get
        {
            if (_client == null)
                _client = new MemcachedClient();

            return _client;
        }
        private set
        {
            _client = value;
        }
    }

    private CommonObjectsCache()
    {
        _client = new MemcachedClient();
    }

    public static CommonObjectsCache Cache
    {
        get
        {
            if (_cache == null)
                _cache = new CommonObjectsCache();

            return _cache;
        }
    }
}
  

В моем DAL я использую их следующим образом :

     public static List<Item1> AllItem1s
    {
        get
        {
            if (CommonObjectsCache.Client.Get<List<Item1>>("AllItem1s") == null)
                RefreshItem1Cache();

            return CommonObjectsCache.Client.Get<List<Item1>>("AllItem1s");
        }
        private set
        {
            CommonObjectsCache.Client.Store(StoreMode.Set, "AllItem1s", value);
        }
    }
    public static List<Item2> AllItem2s
    {
        get {  // Same as above }
        private set { // Same as above }
    }
    public static List<Item3> AllItem3s
    {
        get {  // Same as above }
        private set { // Same as above }
    }
    public static List<Item4> AllItem4s
    {
        get {  // Same as above }
        private set { // Same as above }

    }
  

И заполнить их как :

 public static void RefreshItem1Cache()
{
    List<Item1> items = (from i ctx.Item1
                        select i).ToList();
    AllItem1s = items;
}
  

В моем коде DAL у меня есть метод, подобный :

 public static MyModel GetMyModel(int? id)
{
    // I use AllItem1s here.
}
  

Когда я запускаю код, он иногда говорит об этом AllItem1s.Count == 0 , но когда я устанавливаю точку останова и диагностирую это значение в AllItem1s, я вижу, что оно заполнено. Итак, я обновил код следующим образом, чтобы проверить, не ошибаюсь ли я :

 public static MyModel GetMyModel(int? id)
{
    if (AllItem1s == null || AllItem1s.Count == 0 || AllItem2s == null || AllItem2s.Count == 0 || AllItem3s == null || AllItem3s.Count == 0 || AllItem4s == null || AllItem4s.Count == 0)
    {
        string msg = "Error!!!!!";
    }
    // I use AllItem1s here.
}
  

И, что удивительно, код падает на string msg = "Error!!!!!"; блок!!!

Но когда я помещаю точку останова внутри if блока и наблюдаю за Count свойством каждой коллекции, я вижу, что у них есть числа.

Итак, я пришел к выводу, что при выборке AllItemXs свойства возникает условие гонки. Когда он проверяет условие, по крайней мере, одно из них не было установлено должным образом (что не имеет смысла, поскольку они находятся в одном потоке, и получатель свойства не может вернуть пустую коллекцию).

Кто-нибудь может объяснить, почему это происходит и как преодолеть эту проблему?

Комментарии:

1. Как выглядит метод RefreshItem1Cache();? Является CommonObjectsCache. Где-то использовался установщик клиента? Где используется RefreshGroupCache()?

2. Можете ли вы показать код SearchItem1s ?

3. Это простые вызовы EF. Я обновил код.

4. Так вы на самом деле никогда не используете CommonObjectsCache.Cache ?

Ответ №1:

Ваша одноэлементная реализация не является потокобезопасной. Представьте, что два (или более) потока одновременно проверяют значение null: оба (все) потока инициализируют свой собственный экземпляр CommonObjectsCache.

Вы можете обернуть нулевую проверку и инициализацию экземпляра с помощью инструкции lock или использовать шаблон блокировки с двойной проверкой.

Просто погуглите потокобезопасную реализацию singleton в C #.