之前寫了一篇:無縫的緩存讀取:雙存儲緩存策略,其中使用了兩個存儲地址交替提供緩存數據。
在其中用了兩個存儲指針轉換以達到無縫讀取緩存,在Cat Chen一語提醒之後,想了一想:的確是沒 有必要在緩存中使用兩個存儲指針的,其實一個存儲地址,只要保證寫入時在其它線程就可以。
更改存儲介質至以下兩個屬性:
namespace CHCache {
/// <summary>
/// 緩存介質
/// </summary>
public class Medium {
/// <summary>
/// 存儲區
/// </summary>
public object Store { get; set; }
/// <summary>
/// 是否正在更新
/// </summary>
public bool IsUpdating { get; set; }
}
}
這裡存儲區用於存儲要緩存的實體內容,而IsUpdating則標識其是否正在更新。
對於緩存類,則更改了寫入和讀取方式。
/*
* 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;
}
}
}
其實有的時候思考問題還是不由自主的向著自己的經驗方向刻意安排,這樣通常把問題搞復雜了。
還好有園子裡的朋友幫助,才簡單的解決了問題,這樣的由簡至繁,再由繁衍至簡的過程其實在實際 開發中發生的還真不少。
本文配套源碼