powered by simpleCommunicator - 2.0.55     © 2025 Programmizd 02
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Форумы / WinForms, .Net Framework [игнор отключен] [закрыт для гостей] / пишу логику с кэшем - критикуйте
25 сообщений из 30, страница 1 из 2
пишу логику с кэшем - критикуйте
    #38775707
netivan
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
решил заморочиться с кэшированием. Задача совершенно обыденная - есть сервис, дает данные, нужно кэшировать.Сервис конечно многопоточный.
Вот сейчас реализовано примерно так:
Код: c#
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
 //#1
    internal class TestService
    {
        static MemoryCache _cache = new MemoryCache("test");
        public string GetSomeData(string key)
        {
            var val =(string)_cache.Get(key);
            if (val == null)
            {
                lock (_cache)
                {
                    val = LoadData();
                    _cache.Add(key,val, DateTime.Now.AddDays(1));
                }
                return val;
            }
            return val;
        }
        string LoadData()
        {
            Thread.Sleep(Program.SLEEP_TIME);
            return Guid.NewGuid().ToString();
        }
    }


все бы хорошо, но проблема в том ,что когда сервису с ключом 2 нужны данные,он по сути будет ждать пока тянутся данные с ключом 1 и тд. А тянуться они, например долго. Непосредственно безопасность самого добавления key-value решается за нас memoryCahce.
Короче Уг подумал я и решил погуглить. Накнуля на шаблон RWLockSlim, например string.Intern
Получилось так:
Код: c#
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
 //#3
    internal class TestService3
    {
        static MemoryCache _cache = new MemoryCache("test");
        ReaderWriterLockSlim locker = new ReaderWriterLockSlim();
        public string GetSomeData(string key)
        {
            string val;
            locker.EnterReadLock();
            try
            {
                val = (string) _cache.Get(key);
                if (val != null)
                    return val;
            }
            finally
            {
                locker.ExitReadLock();
            }

            locker.EnterWriteLock();
            try
            {
                val = (string)_cache.Get(key);
                if (val != null)
                    return val;
                val = LoadData();
                _cache.Add(key, val, DateTime.Now.AddDays(1));
                return val;
            }
            finally
            {
                locker.ExitWriteLock();
            }

        }
        string LoadData()
        {
            Thread.Sleep(Program.SLEEP_TIME);
            return Guid.NewGuid().ToString();
        }

    }


Замерял результат на 10 разных ключах, 10 потоков.
ВОт пример замеров:
Код: c#
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
  var keys = Enumerable.Range(1, COUNT).ToList();
            Stopwatch sw = new Stopwatch();
            var threads = keys.Select(key => new Thread(o => service3.GetSomeData(o.ToString()))).ToList();
            sw.Start();
            for (int index = 0; index < keys.Count; index++)
            {
                var key = keys[index];
                threads[index].Start(key);
            }
            threads.ForEach(t=>t.Join());
            sw.Stop();
            Console.WriteLine(sw.Elapsed);
            Console.Read();


Результат огорчает в обоих случаях = +-10 сек (т.е. по секунде на запрос).
И тут пришла в голову мысль лочить "ключи".
Код: c#
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
 //#2
    internal class TestService2
    {
        ConcurrentDictionary<string,object> _keyDict = new ConcurrentDictionary<string, object>();
        static MemoryCache _cache = new MemoryCache("test");
        public string GetSomeData(string key)
        {
            var val = (string)_cache.Get(key);
            if (val == null)
            {
              var lockerObj=_keyDict.GetOrAdd(key, new object());
                lock (lockerObj)
                {
                    val = LoadData();
                    _cache.Add(key, val, DateTime.Now.AddDays(1));
                }
                return val;
            }
            return val;
        }
        string LoadData()
        {
            Thread.Sleep(Program.SLEEP_TIME);
            return Guid.NewGuid().ToString();
        }
    }


Результат +- 1 секунда. Т.е. почти в 10 раз лучше!
Вообщем обсудите, зачмырите, а лучше дайте свой готовый враппер на этот случай! Пошел спать.
...
Рейтинг: 0 / 0
пишу логику с кэшем - критикуйте
    #38775720
cdtyjv
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
netivan ,
Ну нормально, в принципе. Вы таким образом итеративно пришли к подходу, который называется "striped lock" (от слова stripe - полоса).
Если время на прогрев кэша не критично, то я бы вообще убрал ConcurrentDictionary. Например, наткнутся два потока на отсутствие одного и того же ключа - да и хер с ним. Пускай каждый из них сделает по отдельному запросу. Если у вас никто от этого не умрет - то рекомендую сделать именно так.
...
Рейтинг: 0 / 0
пишу логику с кэшем - критикуйте
    #38775877
Arm79
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
1) Если у вас кэш планируется использовать в многопоточной среде, то и накладываться локи должны в нем, а не перекидывать эту ответственность на внешние классы.

2) Почему не какой-нибудь популярный inmemory db?

3)
netivanкогда сервису с ключом 2 нужны данные,он по сути будет ждать пока тянутся данные с ключом 1 и тд. А тянуться они, например долго.
Почему? В вышеприведенном коде возвращается ссылка на string, а не копия строки

4)
netivan
Код: c#
1.
2.
3.
4.
5.
6.
7.
              var lockerObj=_keyDict.GetOrAdd(key, new object());
                lock (lockerObj)
                {
                    val = LoadData();
                    _cache.Add(key, val, DateTime.Now.AddDays(1));
                }
                return val;


Зачем вы LoadData делаете в секции lock?
...
Рейтинг: 0 / 0
пишу логику с кэшем - критикуйте
    #38775912
Фотография hVostt
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
netivan,

Код: c#
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
//#1
    internal class TestService
    {
        static MemoryCache _cache = new MemoryCache("test");
        public string GetSomeData(string key)
        {
            var val =(string)_cache.Get(key);
            if (val == null)
            {
                val = LoadData(); // ДОБАВЛЯЕМ
                if(_cache.Get(key) == null) // ДОБАВЛЯЕМ
                {
                   lock (_cache)
                   {
                       // val = LoadData(); УБИРАЕМ           
                       _cache.Add(key,val, DateTime.Now.AddDays(1));
                     }
                }
                return val;
            }
            return val;
        }
        string LoadData()
        {
            Thread.Sleep(Program.SLEEP_TIME);
            return Guid.NewGuid().ToString();
        }
    }
...
Рейтинг: 0 / 0
пишу логику с кэшем - критикуйте
    #38775915
Фотография hVostt
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Код: c#
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
//#1
    internal class TestService
    {
        static MemoryCache _cache = new MemoryCache("test");
        public string GetSomeData(string key)
        {
            var val =(string)_cache.Get(key);
            if (val == null)
            {
                val = LoadData(); // ДОБАВЛЯЕМ
                lock (_cache)
                {
                    if(_cache.Get(key) == null) // ДОБАВЛЯЕМ
                    {
                       // val = LoadData(); УБИРАЕМ           
                       _cache.Add(key,val, DateTime.Now.AddDays(1));
                     }
                }
                return val;
            }
            return val;
        }
        string LoadData()
        {
            Thread.Sleep(Program.SLEEP_TIME);
            return Guid.NewGuid().ToString();
        }
    }



поправил
...
Рейтинг: 0 / 0
пишу логику с кэшем - критикуйте
    #38775960
netivan
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
hVostt
Код: c#
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
//#1
    internal class TestService
    {
        static MemoryCache _cache = new MemoryCache("test");
        public string GetSomeData(string key)
        {
            var val =(string)_cache.Get(key);
            if (val == null)
            {
                val = LoadData(); // ДОБАВЛЯЕМ
                lock (_cache)
                {
                    if(_cache.Get(key) == null) // ДОБАВЛЯЕМ
                    {
                       // val = LoadData(); УБИРАЕМ           
                       _cache.Add(key,val, DateTime.Now.AddDays(1));
                     }
                }
                return val;
            }
            return val;
        }
        string LoadData()
        {
            Thread.Sleep(Program.SLEEP_TIME);
            return Guid.NewGuid().ToString();
        }
    }



поправил
да, про дабл чек я потом вспомнил. Но если пришел запрос с 2 одинаковыми значениями, Load будет выполняться 2 раза.
...
Рейтинг: 0 / 0
пишу логику с кэшем - критикуйте
    #38775963
netivan
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Arm791) Если у вас кэш планируется использовать в многопоточной среде, то и накладываться локи должны в нем, а не перекидывать эту ответственность на внешние классы.

2) Почему не какой-нибудь популярный inmemory db?

3)
netivanкогда сервису с ключом 2 нужны данные,он по сути будет ждать пока тянутся данные с ключом 1 и тд. А тянуться они, например долго.
Почему? В вышеприведенном коде возвращается ссылка на string, а не копия строки

4)
netivan
Код: c#
1.
2.
3.
4.
5.
6.
7.
              var lockerObj=_keyDict.GetOrAdd(key, new object());
                lock (lockerObj)
                {
                    val = LoadData();
                    _cache.Add(key, val, DateTime.Now.AddDays(1));
                }
                return val;


Зачем вы LoadData делаете в секции lock?
да, с Load косяк) Уже потом понял.
Но тем не менее лочить по ключу помойму хорошая идея.
...
Рейтинг: 0 / 0
пишу логику с кэшем - критикуйте
    #38775970
Фотография tAZAR
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Пока собирался писать - про дабл чек вы уже вспомнили.
MemoryCache (MSDN):
This type is thread safe.
Из коробки должен давать потокобезопасные Add/Get/Remove.
Само же содержимое уже нужно блокировать. lock - самый тяжелый способ.
...
Рейтинг: 0 / 0
пишу логику с кэшем - критикуйте
    #38775972
netivan
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
tAZARПока собирался писать - про дабл чек вы уже вспомнили.
MemoryCache (MSDN):
This type is thread safe.
Из коробки должен давать потокобезопасные Add/Get/Remove.
Само же содержимое уже нужно блокировать. lock - самый тяжелый способ.
вот с ним и думаю как лучше написать "враппер".
Ибо стандратная процедура: Get(key) - нет данных - Получаем- Записываем.
Возможно тут lock лишний вообще не нужен.
http://msdn.microsoft.com/ru-ru/library/system.threading.readerwriterlockslim(v=vs.110).aspx
вот тут пример с Dictionary<>. Возможно тоже самое реализовано в MemoryCache уже.
...
Рейтинг: 0 / 0
пишу логику с кэшем - критикуйте
    #38775976
Arm79
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
netivanда, с Load косяк) Уже потом понял.
Но тем не менее лочить по ключу помойму хорошая идея.
Лочить что-либо вообще не очень хорошая идея, чем больше локов, тем медленнее все работает. Это неизбежное зло, но его нужно минимизировать. Как по общему количеству, так и по действиям, которые в этом локе происходят. Для примера - зачем придумали Concurrent* коллекции, если есть обычные + lock?

Далее, если опустить необходимость блокировки коллекции ключей, я совершенно не вижу причины делать это вне класса MemoryCache. Как и механизм получения данных по ключу.

netivanда, про дабл чек я потом вспомнил. Но если пришел запрос с 2 одинаковыми значениями, Load будет выполняться 2 раза.
Нувыжэксперт, придумайте что-нибудь :-) Например, ведите еще одну коллекцию - пришедших запросов :-)
...
Рейтинг: 0 / 0
пишу логику с кэшем - критикуйте
    #38775990
netivan
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Arm79netivanда, с Load косяк) Уже потом понял.
Но тем не менее лочить по ключу помойму хорошая идея.
Лочить что-либо вообще не очень хорошая идея, чем больше локов, тем медленнее все работает. Это неизбежное зло, но его нужно минимизировать. Как по общему количеству, так и по действиям, которые в этом локе происходят. Для примера - зачем придумали Concurrent* коллекции, если есть обычные + lock?

Далее, если опустить необходимость блокировки коллекции ключей, я совершенно не вижу причины делать это вне класса MemoryCache. Как и механизм получения данных по ключу.

netivanда, про дабл чек я потом вспомнил. Но если пришел запрос с 2 одинаковыми значениями, Load будет выполняться 2 раза.
Нувыжэксперт, придумайте что-нибудь :-) Например, ведите еще одну коллекцию - пришедших запросов :-)
дык я и думаю. Про lock я согласен - чем меньше тем лучше :).
Никто не в курсе что внутри MemoryCache? Или рефлектор открывать)
авторНапример, ведите еще одну коллекцию - пришедших запросов :-)
ну типа это я и сделал:
Код: c#
1.
2.
3.
4.
5.
6.
7.
 var lockerObj=_keyDict.GetOrAdd(key, new object());
                lock (lockerObj)
                {
                    val = LoadData();
                    _cache.Add(key, val, DateTime.Now.AddDays(1));
                }
                return val;

+ только дабл чек добавить
...
Рейтинг: 0 / 0
пишу логику с кэшем - критикуйте
    #38776020
Фотография hVostt
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
netivanда, про дабл чек я потом вспомнил. Но если пришел запрос с 2 одинаковыми значениями, Load будет выполняться 2 раза.

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

суть в том, чтобы взять данные из кеша, если он там есть или из источника данных. и никого при этом не ждать. иначе огребёте по полной.

дабл чек нужен, чтобы не ложить в кеш данные два раза, больше ни на что это не влияет.
...
Рейтинг: 0 / 0
пишу логику с кэшем - критикуйте
    #38776028
netivan
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
hVosttnetivanда, про дабл чек я потом вспомнил. Но если пришел запрос с 2 одинаковыми значениями, Load будет выполняться 2 раза.

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

суть в том, чтобы взять данные из кеша, если он там есть или из источника данных. и никого при этом не ждать. иначе огребёте по полной.

дабл чек нужен, чтобы не ложить в кеш данные два раза, больше ни на что это не влияет.
ну зато грузим сервер 2 раза :) А может там запрос 30 минут выполняется?!
...
Рейтинг: 0 / 0
пишу логику с кэшем - критикуйте
    #38776033
Фотография Где-то в степи
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
netivan,
уж не знаю что Вы кешируете, уж не говорите что запросы к базе и результаты ответов, а то я войду в ступор..
ЧТо кешируем?
...
Рейтинг: 0 / 0
пишу логику с кэшем - критикуйте
    #38776034
netivan
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Где-то в степиnetivan,
уж не знаю что Вы кешируете, уж не говорите что запросы к базе и результаты ответов, а то я войду в ступор..
ЧТо кешируем?
ну примерно. Только это запрос не к БД, а к другой системе. Например, история операций. Раз в Х минут она обновляется из кеша.
...
Рейтинг: 0 / 0
пишу логику с кэшем - критикуйте
    #38776035
Фотография tAZAR
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
netivantAZARПока собирался писать - про дабл чек вы уже вспомнили.
MemoryCache (MSDN):
This type is thread safe.
Из коробки должен давать потокобезопасные Add/Get/Remove.
Само же содержимое уже нужно блокировать. lock - самый тяжелый способ.
вот с ним и думаю как лучше написать "враппер".
Ибо стандратная процедура: Get(key) - нет данных - Получаем- Записываем.
Возможно тут lock лишний вообще не нужен.
http://msdn.microsoft.com/ru-ru/library/system.threading.readerwriterlockslim(v=vs.110).aspx
вот тут пример с Dictionary<>. Возможно тоже самое реализовано в MemoryCache уже.

Именно этот лок лишний.
С содержимым достаточно быстро будет работать RW lock или по ситуации уже, смотря что кешируете.
...
Рейтинг: 0 / 0
пишу логику с кэшем - критикуйте
    #38776039
netivan
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
tAZARnetivanпропущено...

вот с ним и думаю как лучше написать "враппер".
Ибо стандратная процедура: Get(key) - нет данных - Получаем- Записываем.
Возможно тут lock лишний вообще не нужен.
http://msdn.microsoft.com/ru-ru/library/system.threading.readerwriterlockslim(v=vs.110).aspx
вот тут пример с Dictionary<>. Возможно тоже самое реализовано в MemoryCache уже.

Именно этот лок лишний.
С содержимым достаточно быстро будет работать RW lock или по ситуации уже, смотря что кешируете.
не понял, какой лишний?
...
Рейтинг: 0 / 0
пишу логику с кэшем - критикуйте
    #38776040
Фотография Где-то в степи
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
netivan,
ну а че ключ строка? на словах что ли передаете?
...
Рейтинг: 0 / 0
пишу логику с кэшем - критикуйте
    #38776044
Фотография hVostt
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
netivanну зато грузим сервер 2 раза :) А может там запрос 30 минут выполняется?!

какая разница? нагрузку с сервера вы существенно таким образом не снимите, зато поимеете проблем. зачем это вам надо? кроме того, говорить о нагрузках без тестирования не имеет смысла.

ещё раз. суть кеширования, это отдать данные из кеша если они там есть . всё.
не стоит превращать кеш в промежуточный источник данных.
...
Рейтинг: 0 / 0
пишу логику с кэшем - критикуйте
    #38776045
netivan
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Где-то в степиnetivan,
ну а че ключ строка? на словах что ли передаете?
ну там GetHasCode(), но разве это принципиально?
...
Рейтинг: 0 / 0
пишу логику с кэшем - критикуйте
    #38776074
Фотография Где-то в степи
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
netivan,
принципиально как вы подходите к этому. если с парадигмы распределенных вычислений близко к ста процентам все в конце концов
замыкается на базе тут кеширование выгодней уложить на плечи орм и в общем то все они прекрасно это поддерживают из коробки,
ну кинут в меня камень что недоОрм EF это не умеет, но есть примеры где прикручивают второй уровень и к этому чуду,( о последней версии умолчу), если с точки зрения , как говорит МСУ, трех звенки в ипостасии, то топик имеет актуальность все зависит как смотреть..
...
Рейтинг: 0 / 0
пишу логику с кэшем - критикуйте
    #38776082
Фотография tAZAR
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
netivan,

У вас он сейчас 1, на Add, который заявлен потокобезопасным.
Сам этот кеш использовал с блокировками содержимого через rw lock, conditional variable (ну, была там одна ситуация..) и неблокирующими операциями с содержимым. Стандартные операции не лочили никогда.
...
Рейтинг: 0 / 0
пишу логику с кэшем - критикуйте
    #38776090
Фотография hVostt
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Где-то в степину кинут в меня камень что недоОрм EF это не умеет

лови камень

https://efcache.codeplex.com/
http://msdn.microsoft.com/ru-ru/magazine/hh394143.aspx

всё там есть.
...
Рейтинг: 0 / 0
пишу логику с кэшем - критикуйте
    #38776096
Фотография Где-то в степи
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
netivan
ну вот видишь, и оно тоже умеет ))
...
Рейтинг: 0 / 0
пишу логику с кэшем - критикуйте
    #38776136
netivan
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Где-то в степиnetivan
ну вот видишь, и оно тоже умеет ))
у меня нет никакого EF, даже БД нет=).
+ есть ручное Policy обновления, к сожалению.
Что имеем то имеем.
Вот смотрю на пример с RWLock и если честно не очень понимаю зачем там так накрутили. http://msdn.microsoft.com/ru-ru/library/system.threading.readerwriterlockslim(v=vs.110).aspx
если есть Concurrent. Видимо только для примера.
Вроде бы понял,что локов в MemoryCache достаточно.
...
Рейтинг: 0 / 0
25 сообщений из 30, страница 1 из 2
Форумы / WinForms, .Net Framework [игнор отключен] [закрыт для гостей] / пишу логику с кэшем - критикуйте
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]