#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 #.