コード例 #1
0
        /// <summary>
        /// Потокобезопасно берет объект из кэша, если его там нет, то вызывает функцию для получения данных
        /// и кладет результат в кэш
        /// </summary>
        /// <typeparam name="T">тип объектов в кэше</typeparam>
        /// <param name="provider">провайдер кэша</param>
        /// <param name="key">тэг, в общем случае представляет имя класса сервиса + имя метода + список параметров</param>
        /// <param name="tags">список зависимых контентов</param>
        /// <param name="expiration">время жизни в кэше</param>
        /// <param name="getData">функция для получения данных, если объектов кэше нет. нужно использовать анонимный делегат</param>
        /// <returns>закэшированне данные, если они присутствуют в кэше или результат выполнения функции</returns>
        public static T GetOrAdd <T>(this IVersionedCacheProvider provider, string key, string[] tags, TimeSpan expiration, Func <T> getData)
        {
            var    supportCallbacks = _vProviderType.Value;
            object result           = provider.Get(key, tags);
            object deprecatedResult = null;

            if (result == null)
            {
                object localLocker = _lockers.GetOrAdd(key, _ => new object());
                bool   lockTaken   = false;
                try
                {
                    Stopwatch sw = new Stopwatch();
                    sw.Start();

                    if (supportCallbacks)
                    {
                        // проверяем, что есть предыдущее значение
                        deprecatedResult = provider.Get(VersionedCacheProvider3.CalculateDeprecatedKey(key));

                        if (deprecatedResult != null)
                        {
                            Monitor.TryEnter(localLocker, ref lockTaken);
                        }
                        else
                        {
                            Monitor.TryEnter(localLocker, TRYENTER_TIMEOUT_MS, ref lockTaken);
                        }
                    }
                    else
                    {
                        Monitor.TryEnter(localLocker, TRYENTER_TIMEOUT_MS, ref lockTaken);
                    }

                    if (lockTaken)
                    {
                        result = provider.Get(key, tags);
                        var time1 = sw.ElapsedMilliseconds;
                        if (result == null)
                        {
                            DateTime startT = DateTime.Now;
                            result = getData();
                            sw.Stop();
                            var time2 = sw.ElapsedMilliseconds;

                            CheckPerformance(key, time1, time2);

                            if (result != null)
                            {
                                provider.Add(result, key, tags, expiration);
                                if (supportCallbacks && deprecatedResult != null)
                                {
                                    provider.Invalidate(VersionedCacheProvider3.CalculateDeprecatedKey(key));
                                }
                            }
                        }
                    }
                    else
                    {
                        if (supportCallbacks && deprecatedResult != null)
                        {
                            return(Convert <T>(deprecatedResult));
                        }

                        var time1 = sw.ElapsedMilliseconds;
                        Logger.Log(() => string.Format("Долгое нахождение в ожидании обновления кэша {1} ms, ключ: {0} ", key, time1),
                                   EventLevel.Warning);

                        result = getData();
                        sw.Stop();
                        var time2 = sw.ElapsedMilliseconds;

                        CheckPerformance(key, time1, time2);

                        if (result != null)
                        {
                            provider.Add(result, key, tags, expiration);
                            if (supportCallbacks && deprecatedResult != null)
                            {
                                provider.Invalidate(VersionedCacheProvider3.CalculateDeprecatedKey(key));
                            }
                        }
                    }
                }
                finally
                {
                    if (lockTaken)
                    {
                        Monitor.Exit(localLocker);
                    }
                }
            }

            return(Convert <T>(result));
        }
コード例 #2
0
        /// <summary>
        /// Потокобезопасно берет объект из кэша, если его там нет, то вызывает функцию для получения данных
        /// и кладет результат в кэш
        /// </summary>
        /// <typeparam name="T">тип объектов в кэше</typeparam>
        /// <param name="provider">провайдер кэша</param>
        /// <param name="key">тэг, в общем случае представляет имя класса сервиса + имя метода + список параметров</param>
        /// <param name="expiration">время жизни в кэше</param>
        /// <param name="getData">функция для получения данных, если объектов кэше нет. нужно использовать анонимный делегат</param>
        /// <returns>закэшированне данные, если они присутствуют в кэше или результат выполнения функции</returns>
        public static T GetOrAdd <T>(this ICacheProvider provider, string key, TimeSpan expiration, Func <T> getData)
        {
            var    supportCallbacks = _providerType.Value;
            object result           = provider.Get(key);
            object deprecatedResult = null;

            if (result == null)
            {
                object localLocker = _lockers.GetOrAdd(key, new object());

                bool lockTaken = false;
                try
                {
                    Stopwatch sw = new Stopwatch();
                    sw.Start();


                    if (supportCallbacks)
                    {
                        // проверяем, что есть предыдущее значение
                        deprecatedResult = provider.Get(VersionedCacheProvider3.CalculateDeprecatedKey(key));

                        if (deprecatedResult != null)
                        {
                            // если есть, то обновлять данные будет только 1 поток
                            Monitor.TryEnter(localLocker, ref lockTaken);
                        }
                        else
                        {
                            Monitor.TryEnter(localLocker, TRYENTER_TIMEOUT_MS, ref lockTaken);
                        }
                    }
                    else
                    {
                        Monitor.TryEnter(localLocker, TRYENTER_TIMEOUT_MS, ref lockTaken);
                    }

                    if (lockTaken)
                    {
                        result = provider.Get(key);

                        var time1 = sw.ElapsedMilliseconds;

                        if (result == null)
                        {
                            result = getData();
                            sw.Stop();
                            var time2 = sw.ElapsedMilliseconds;

                            CheckPerformance(key, time1, time2);

                            if (result != null)
                            {
                                provider.Set(key, result, expiration);
                                if (supportCallbacks && deprecatedResult != null)
                                {
                                    // если был устаревший объект в кеше, то удалим его
                                    provider.Invalidate(VersionedCacheProvider3.CalculateDeprecatedKey(key));
                                }
                            }
                        }
                    }
                    else
                    {
                        if (supportCallbacks && deprecatedResult != null)
                        {
                            return(Convert <T>(deprecatedResult));
                        }

                        var time1 = sw.ElapsedMilliseconds;
                        Logger.Log(() => $"Долгое нахождение в ожидании обновления кэша {time1} ms, ключ: {key} ", EventLevel.Warning);

                        result = getData();

                        sw.Stop();
                        var time2 = sw.ElapsedMilliseconds;

                        CheckPerformance(key, time1, time2, reportTime1: false);

                        if (result != null)
                        {
                            provider.Set(key, result, expiration);

                            if (supportCallbacks && deprecatedResult != null)
                            {
                                provider.Invalidate(VersionedCacheProvider3.CalculateDeprecatedKey(key));
                            }
                        }
                    }
                }
                finally
                {
                    if (lockTaken)
                    {
                        Monitor.Exit(localLocker);
                    }
                }
            }
            return(Convert <T>(result));
        }
コード例 #3
0
        /// <summary>
        /// Потокобезопасно берет объект из кэша, если его там нет, то вызывает асинхронную функцию для получения данных
        /// и кладет результат в кэш.
        /// ВАЖНО: не поддерживается рекурсивный вызов с одинаковыми ключами (ограничение SemaphoreSlim).
        /// В случае вложенного вызова с одинаковым ключом возникнет таймаут длительностью 7 секунд
        /// </summary>
        /// <typeparam name="T">тип объектов в кэше</typeparam>
        /// <param name="provider">провайдер кэша</param>
        /// <param name="key">тэг, в общем случае представляет имя класса сервиса + имя метода + список параметров</param>
        /// <param name="expiration">время жизни в кэше</param>
        /// <param name="getData">функция для получения данных, если объектов кэше нет. нужно использовать асинхронный анонимный делегат</param>
        /// <returns>закэшированне данные, если они присутствуют в кэше или результат выполнения функции</returns>
        public static async Task <T> GetOrAddAsync <T>(this ICacheProvider provider, string key, TimeSpan expiration, Func <Task <T> > getData)
        {
            var    supportCallbacks = _providerType.Value;
            object result           = provider.Get(key);
            object deprecatedResult = null;

            if (result == null)
            {
                SemaphoreSlim localLocker = _semaphores.GetOrAdd(key, _ => new SemaphoreSlim(1));

                bool lockTaken = false;
                try
                {
                    Stopwatch sw = new Stopwatch();
                    sw.Start();

                    if (supportCallbacks)
                    {
                        // проверяем, что есть предыдущее значение
                        deprecatedResult = provider.Get(VersionedCacheProvider3.CalculateDeprecatedKey(key));

                        if (deprecatedResult != null)
                        {
                            // если есть, то обновлять данные будет только 1 поток
                            lockTaken = await localLocker
                                        .WaitAsync(DEPRECATEDRESULT_TIMEOUT_MS)
                                        .ConfigureAwait(false);
                        }
                        else
                        {
                            lockTaken = await localLocker
                                        .WaitAsync(TimeSpan.FromMilliseconds(TRYENTER_TIMEOUT_MS))
                                        .ConfigureAwait(false);;
                        }
                    }
                    else
                    {
                        lockTaken = await localLocker
                                    .WaitAsync(TimeSpan.FromMilliseconds(TRYENTER_TIMEOUT_MS))
                                    .ConfigureAwait(false);;
                    }

                    if (lockTaken)
                    {
                        result = provider.Get(key);

                        var time1 = sw.ElapsedMilliseconds;

                        if (result == null)
                        {
                            result = await getData().ConfigureAwait(false);

                            sw.Stop();
                            var time2 = sw.ElapsedMilliseconds;

                            CheckPerformance(key, time1, time2, false);

                            if (result != null)
                            {
                                provider.Set(key, result, expiration);
                                if (supportCallbacks && deprecatedResult != null)
                                {
                                    // если был устаревший объект в кеше, то удалим его
                                    provider.Invalidate(VersionedCacheProvider3.CalculateDeprecatedKey(key));
                                }
                            }
                        }
                    }
                    else
                    {
                        if (supportCallbacks && deprecatedResult != null)
                        {
                            return(Convert <T>(deprecatedResult));
                        }

                        var time1 = sw.ElapsedMilliseconds;
                        Logger.Log(() => $"Долгое нахождение в ожидании обновления кэша {time1} ms, ключ: {key} ", EventLevel.Warning);

                        result = await getData().ConfigureAwait(false);

                        sw.Stop();
                        var time2 = sw.ElapsedMilliseconds;

                        CheckPerformance(key, time1, time2, false);

                        if (result != null)
                        {
                            provider.Set(key, result, expiration);
                        }
                    }
                }
                finally
                {
                    if (lockTaken)
                    {
                        localLocker.Release();
                    }
                }
            }
            return(Convert <T>(result));
        }