今天編寫了一個采用ASP.NET Caching的組件,在為它編寫Unit Test的過程中發現了一個有趣的問題,接下來我通過一個簡單的實例說明這個問題。我們在一個控制臺應用中編寫了如下一段程序,這個段程序很簡單:我們通過HttpRuntime的靜態屬性Cache得到表示當前緩存的Cache對象,并調用其Insert方法對當前的時間實施緩存。需要注意的是,我們采用“滑動時間”過期策略,并將這個滑動時間設置為1秒。
1: class Program
2: {
3: static void Main(string[] args)
4: {
5: string key = Guid.NewGuid().ToString();
6: HttpRuntime.Cache.Insert(key, DateTime.Now, null, Cache.NoAbsoluteExpiration, new TimeSpan(0, 0, 1) );
7: for (int i = 0; i < 5; i++)
8: {
9: Console.WriteLine(HttpRuntime.Cache.Get(key)??"N/A");
10: Thread.Sleep(500);
11: }
12: }
13: }
接下來我們在一個for循環中提取緩存的時間并將其顯示在控制臺上,每次迭代之后會有0.5秒的休眠時間。根據緩存針對滑動時間過期策略,由于我們每隔0.5秒會讀取緩存,所以在這段時間內緩存是不會過期的。但是如下所示的執行結果告訴我們,添加的緩存在1秒之后過期了。
1: 4/1/2014 2:51:12 PM
2: 4/1/2014 2:51:12 PM
3: N/A
4: N/A
5: N/A
是否是ASP.NET緩存機制錯了什么問題呢?其實不是,真正的原因是我們將滑動過期時間范圍設置得太小了。為了證實這一點,我們按照如下的方式將這個時間設置為2秒。
1: class Program
2: {
3: static void Main(string[] args)
4: {
5: string key = Guid.NewGuid().ToString();
6: HttpRuntime.Cache.Insert(key, DateTime.Now, null, Cache.NoAbsoluteExpiration, new TimeSpan(0, 0, 2) );
7: for (int i = 0; i < 5; i++)
8: {
9: Console.WriteLine(HttpRuntime.Cache.Get(key)??"N/A");
10: Thread.Sleep(1000);
11: }
12: }
13: }
再次運行我們的程序后會的如下所示的輸出結果,我們可以看到添加的緩存并沒有過期。
1: 4/1/2014 2:59:15 PM
2: 4/1/2014 2:59:15 PM
3: 4/1/2014 2:59:15 PM
4: 4/1/2014 2:59:15 PM
5: 4/1/2014 2:59:15 PM
通過查看相關源代碼,我們發現這個問題的根源所在:如果我們調用Cache的Insert或者Add方法時指定了其slidingExpiration參數,針對該緩存項的每次提取操作,系統都會修改緩存項的過期時間(當前時間+slidingExpiration)。但是過期時間的修改是由前提的:它要求這個slidingExpiration參數指定的時間必須大于設定的最小時間,這個時間對應著內部類型CacheExpires具有如下定義的靜態只讀屬性TimeSpan MIN_UPDATE_DELTA ,我們可以看到它的時間跨度正是1秒。所以如果我們指定的slidingExpiration參數小于1秒,實際上起不到“滑動過期 ”的作用。當然,在真實的項目中我們并不會將滑動時間設置的如此之短。
1: internal sealed class CacheExpires
2: {
3: //其他成員
4: internal static readonly TimeSpan MIN_UPDATE_DELTA = new TimeSpan(0, 0, 1);
5: }
文章列表