以前寫了一篇:無縫的緩存讀取:雙存儲緩存購物網站排名策略,其中使用了兩個存儲地址交替提供緩存數據。緩存
在其中用了兩個存儲指針轉換以達到無縫讀取緩存,在Cat Chen一語提醒以後,想了一想:的確是沒有必要在緩存中使用兩個存儲指針的,其實一個存儲地址,只要保證寫入時在其它線程就能夠。函數
更改存儲介質至如下兩個屬性:網站
namespace CHCache { /// <summary> /// 緩存介質 /// </summary> public class Medium { /// <summary> /// 存儲區 /// </summary> public object Store { get; set; } /// <summary> /// 是否正在更新 /// </summary> public bool IsUpdating { get; set; } } }
這裏存儲區用於存儲要緩存的實體內容,而IsUpdating則標識其是否正在更新。this
對於緩存類,則更改了寫入和讀取方式。spa
/* * http://www.cnblogs.com/chsword/ * chsword * Date: 2009-3-31 * Time: 17:00 * */ using System;using System.Collections;using System.Collections.Generic;using System.Threading;namespace CHCache { /// <summary> /// 雙存儲的類 /// </summary> public class DictionaryCache : IEnumerable { /// <summary> /// 在此緩存構造時初始化字典對象 /// </summary> public DictionaryCache() { Store = new Dictionary<string, Medium>(); } public void Add(string key, Func<object> func) { if (Store.ContainsKey(key)) {//修改,若是已經存在,再次添加時則採用其它線程 var elem = Store[key]; if (elem.IsUpdating) return; //正在寫入未命中 var th = new ThreadHelper(elem, func); var td = new Thread(th.Doit); td.Start(); } else {//首次添加時可能也要讀取,因此要本線程執行 Console.WriteLine("Begin first write"); Store.Add(key, new Medium { Store = func() }); Console.WriteLine("End first write"); } } /// <summary> /// 讀取時所用的索引 /// </summary> /// <param name="key"></param> /// <returns></returns> public object this[string key] { get { if (!Store.ContainsKey(key)) return null; var elem = Store[key]; return elem.Store; } } Dictionary<string, Medium> Store { get; set; } public IEnumerator GetEnumerator() { return ((IEnumerable)Store).GetEnumerator(); } } }
這裏在添加時只控制了首次寫入緩存在主線程,而讀取時則直接讀取緩存內容。線程
而線程輔助類也進行了簡化,僅將其執行並寫入就行了,拋出線程徹底由DictionaryCache控制。指針
using System;namespace CHCache { /// <summary> /// 一個線程Helper,用於幫助多拋出線程時傳遞參數 /// </summary> public class ThreadHelper { Func<object> Fun { get; set; } Medium Medium { get; set; } /// <summary> /// 經過構造函數來傳遞參數 /// </summary> /// <param name="m"></param> /// <param name="fun"></param> public ThreadHelper(Medium m, Func<object> fun) { Medium = m; Fun = fun; } /// <summary> /// 線程入口,ThreadStart委託所對應的方法 /// </summary> public void Doit() { Medium.IsUpdating = true; Console.WriteLine("Begin write."); var ret = Fun.Invoke(); Medium.Store = ret; Console.WriteLine("End write."); Medium.IsUpdating = false; } } }
其實有的時候思考問題仍是不禁自主的向着本身的經驗方向刻意安排,這樣一般把問題搞複雜了。code
還好有園子裏的朋友幫助,才簡單的解決了購物網站大全問題,這樣的由簡至繁,再由繁衍至簡的過程其實在實際開發中發生的還真很多。對象