491 lines
14 KiB
C#
491 lines
14 KiB
C#
|
using System;
|
|||
|
using System.Collections.Generic;
|
|||
|
|
|||
|
using System.Text;
|
|||
|
|
|||
|
namespace Thousandto.Core.Base
|
|||
|
{
|
|||
|
|
|||
|
public interface IGcSweepingStrategy
|
|||
|
{
|
|||
|
bool Sweepable();
|
|||
|
bool Sweepable(object value);
|
|||
|
}
|
|||
|
|
|||
|
public interface IGcGenerationStrategy
|
|||
|
{
|
|||
|
int GetMaxGenerationCount();
|
|||
|
bool NextGeneration(int gen, int lived);
|
|||
|
bool Collectable(int gen, int last);
|
|||
|
}
|
|||
|
|
|||
|
public class GcGenerationStrategy : IGcGenerationStrategy
|
|||
|
{
|
|||
|
public const int MAX_GENERATION_INDEX = 32;
|
|||
|
public static int Now()
|
|||
|
{
|
|||
|
return System.Environment.TickCount;
|
|||
|
}
|
|||
|
public int GetMaxGenerationCount()
|
|||
|
{
|
|||
|
return MAX_GENERATION_INDEX;
|
|||
|
}
|
|||
|
public bool NextGeneration(int gen, int lived)
|
|||
|
{
|
|||
|
return lived > 60000 * gen;
|
|||
|
}
|
|||
|
public bool Collectable(int gen, int last)
|
|||
|
{
|
|||
|
return Now() - last > 60000 * gen;
|
|||
|
}
|
|||
|
public static GcGenerationStrategy sharedInstance = new GcGenerationStrategy();
|
|||
|
}
|
|||
|
|
|||
|
public class GcSweepingStrategy : IGcSweepingStrategy
|
|||
|
{
|
|||
|
int _past = 0;
|
|||
|
int _interval = 0;
|
|||
|
internal const int DefaultInterval = 8000; //8s
|
|||
|
public GcSweepingStrategy(int intervalMS = 0)
|
|||
|
{
|
|||
|
if (intervalMS <= 0)
|
|||
|
{
|
|||
|
intervalMS = DefaultInterval;
|
|||
|
}
|
|||
|
_interval = intervalMS;
|
|||
|
_past = System.Environment.TickCount;
|
|||
|
}
|
|||
|
public virtual bool Sweepable(object value)
|
|||
|
{
|
|||
|
return true;
|
|||
|
}
|
|||
|
public virtual bool Sweepable()
|
|||
|
{
|
|||
|
bool result = false;
|
|||
|
int now = System.Environment.TickCount;
|
|||
|
if (now - _past > _interval)
|
|||
|
{
|
|||
|
result = true;
|
|||
|
_past = now;
|
|||
|
}
|
|||
|
return result;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public enum DataCacheCollectableFlag
|
|||
|
{
|
|||
|
None = 0,
|
|||
|
No = 1,
|
|||
|
Yes = 2,
|
|||
|
Collectable = 4,
|
|||
|
Touching = 8,
|
|||
|
}
|
|||
|
|
|||
|
public class DataCache<TKey, TValue>
|
|||
|
{
|
|||
|
class Item
|
|||
|
{
|
|||
|
internal TValue value = default(TValue);
|
|||
|
internal int lastTouchedTime = 0;
|
|||
|
internal int livedTime = 0;
|
|||
|
internal int generation = 0;
|
|||
|
internal bool immortal = false;
|
|||
|
internal DataCache<TKey, TValue> owner;
|
|||
|
|
|||
|
internal void Assign(TValue source, int gen = 1, int now = 0)
|
|||
|
{
|
|||
|
if (now <= 0)
|
|||
|
{
|
|||
|
now = GcGenerationStrategy.Now();
|
|||
|
}
|
|||
|
value = source;
|
|||
|
generation = gen;
|
|||
|
lastTouchedTime = now;
|
|||
|
livedTime = 0;
|
|||
|
}
|
|||
|
internal void Touch()
|
|||
|
{
|
|||
|
var n = GcGenerationStrategy.Now();
|
|||
|
var e = n - lastTouchedTime;
|
|||
|
lastTouchedTime = n;
|
|||
|
livedTime += e;
|
|||
|
if (owner._gen.NextGeneration(generation, livedTime) && generation + 1 <= owner._gen.GetMaxGenerationCount())
|
|||
|
{
|
|||
|
++generation;
|
|||
|
}
|
|||
|
}
|
|||
|
internal Item(TValue source, DataCache<TKey, TValue> _owner)
|
|||
|
{
|
|||
|
owner = _owner;
|
|||
|
Assign(source, 1, GcGenerationStrategy.Now());
|
|||
|
}
|
|||
|
internal bool Collectable()
|
|||
|
{
|
|||
|
return immortal == false && owner._gen.Collectable(generation, lastTouchedTime);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
IGcSweepingStrategy _sweeper = null;
|
|||
|
IGcGenerationStrategy _gen = null;
|
|||
|
MyAction<TValue> _releaseValueFunc = null;
|
|||
|
MyFunc<TValue, DataCacheCollectableFlag> _collectableFunc = null;
|
|||
|
Dictionary<TKey, Item> _tank = null;
|
|||
|
|
|||
|
Dictionary<TKey, Item> tank
|
|||
|
{
|
|||
|
get
|
|||
|
{
|
|||
|
if (_tank == null)
|
|||
|
{
|
|||
|
_tank = new Dictionary<TKey, Item>();
|
|||
|
}
|
|||
|
return _tank;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public DataCache(int sweepIntervalMS = -1,
|
|||
|
IGcSweepingStrategy sweeper = null,
|
|||
|
IGcGenerationStrategy gen = null,
|
|||
|
MyAction<TValue> valueReleaser = null,
|
|||
|
MyFunc<TValue, DataCacheCollectableFlag> collectableFunc = null)
|
|||
|
{
|
|||
|
_sweeper = sweeper;
|
|||
|
_releaseValueFunc = valueReleaser;
|
|||
|
_collectableFunc = collectableFunc;
|
|||
|
if (_sweeper == null)
|
|||
|
{
|
|||
|
if (sweepIntervalMS < 0)
|
|||
|
{
|
|||
|
sweepIntervalMS = GcSweepingStrategy.DefaultInterval;
|
|||
|
}
|
|||
|
_sweeper = new GcSweepingStrategy(sweepIntervalMS);
|
|||
|
}
|
|||
|
_gen = gen ?? GcGenerationStrategy.sharedInstance;
|
|||
|
}
|
|||
|
|
|||
|
~DataCache()
|
|||
|
{
|
|||
|
Clear();
|
|||
|
}
|
|||
|
|
|||
|
Item GetItem(TKey key)
|
|||
|
{
|
|||
|
Item item = null;
|
|||
|
if (_tank != null && _tank.TryGetValue(key, out item))
|
|||
|
{
|
|||
|
item.Touch();
|
|||
|
return item;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
return null;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
void _ReleaseValue(TValue value)
|
|||
|
{
|
|||
|
if (_releaseValueFunc != null)
|
|||
|
{
|
|||
|
_releaseValueFunc(value);
|
|||
|
value = default(TValue);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
int _DoSweep(bool force)
|
|||
|
{
|
|||
|
int result = 0;
|
|||
|
if (_tank != null)
|
|||
|
{
|
|||
|
List<TKey> _removeList = null;
|
|||
|
var e = _tank.GetEnumerator();
|
|||
|
try
|
|||
|
{
|
|||
|
while (e.MoveNext())
|
|||
|
{
|
|||
|
var item = e.Current;
|
|||
|
if (force)
|
|||
|
{
|
|||
|
item.Value.immortal = false;
|
|||
|
}
|
|||
|
if (item.Value.Collectable())
|
|||
|
{
|
|||
|
if (_collectableFunc != null)
|
|||
|
{
|
|||
|
var ret = _collectableFunc(item.Value.value);
|
|||
|
if (ret != DataCacheCollectableFlag.None)
|
|||
|
{
|
|||
|
if ((ret & DataCacheCollectableFlag.No) != 0)
|
|||
|
{
|
|||
|
if ((ret & DataCacheCollectableFlag.Touching) != 0)
|
|||
|
{
|
|||
|
item.Value.Touch();
|
|||
|
}
|
|||
|
continue;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
if (_removeList == null)
|
|||
|
{
|
|||
|
_removeList = new List<TKey>();
|
|||
|
}
|
|||
|
_ReleaseValue(item.Value.value);
|
|||
|
_removeList.Add(item.Key);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
finally
|
|||
|
{
|
|||
|
e.Dispose();
|
|||
|
}
|
|||
|
|
|||
|
if (_removeList != null)
|
|||
|
{
|
|||
|
for (int i = 0; i < _removeList.Count; ++i)
|
|||
|
{
|
|||
|
if (_tank.Remove(_removeList[i]))
|
|||
|
{
|
|||
|
result += 1;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
return result;
|
|||
|
}
|
|||
|
|
|||
|
public bool SetImmortal(TKey key, bool enable = true)
|
|||
|
{
|
|||
|
var item = GetItem(key);
|
|||
|
if (item != null)
|
|||
|
{
|
|||
|
item.immortal = enable;
|
|||
|
return true;
|
|||
|
}
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
public TValue Get(TKey key, bool sweep = true)
|
|||
|
{
|
|||
|
var item = GetItem(key);
|
|||
|
var ret = item != null ? item.value : default(TValue);
|
|||
|
if (sweep)
|
|||
|
{
|
|||
|
Sweep();
|
|||
|
}
|
|||
|
return ret;
|
|||
|
}
|
|||
|
|
|||
|
public bool Remove(TKey key)
|
|||
|
{
|
|||
|
Item item = null;
|
|||
|
if (_tank != null && _tank.TryGetValue(key, out item))
|
|||
|
{
|
|||
|
if (_releaseValueFunc != null)
|
|||
|
{
|
|||
|
_releaseValueFunc(item.value);
|
|||
|
}
|
|||
|
return _tank.Remove(key);
|
|||
|
}
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
public bool Add(TKey key, TValue value)
|
|||
|
{
|
|||
|
if (value == null)
|
|||
|
{
|
|||
|
return false;
|
|||
|
}
|
|||
|
var dict = tank;
|
|||
|
Item item = null;
|
|||
|
if (dict.TryGetValue(key, out item))
|
|||
|
{
|
|||
|
item.Touch();
|
|||
|
return false;
|
|||
|
}
|
|||
|
item = new Item(value, this);
|
|||
|
dict.Add(key, item);
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
public bool TryGetValue(TKey key, out TValue value, bool sweep = true)
|
|||
|
{
|
|||
|
if (_tank != null)
|
|||
|
{
|
|||
|
Item item = null;
|
|||
|
if (_tank.TryGetValue(key, out item))
|
|||
|
{
|
|||
|
item.Touch();
|
|||
|
value = item.value;
|
|||
|
if (sweep)
|
|||
|
{
|
|||
|
Sweep();
|
|||
|
}
|
|||
|
return true;
|
|||
|
}
|
|||
|
}
|
|||
|
value = default(TValue);
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
public void Clear()
|
|||
|
{
|
|||
|
if (_tank != null)
|
|||
|
{
|
|||
|
if (_releaseValueFunc != null)
|
|||
|
{
|
|||
|
var e = _tank.GetEnumerator();
|
|||
|
try
|
|||
|
{
|
|||
|
while (e.MoveNext())
|
|||
|
{
|
|||
|
_releaseValueFunc(e.Current.Value.value);
|
|||
|
}
|
|||
|
}
|
|||
|
finally
|
|||
|
{
|
|||
|
e.Dispose();
|
|||
|
}
|
|||
|
}
|
|||
|
_tank.Clear();
|
|||
|
_tank = null;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public int Count
|
|||
|
{
|
|||
|
get
|
|||
|
{
|
|||
|
if (_tank != null)
|
|||
|
{
|
|||
|
return _tank.Count;
|
|||
|
}
|
|||
|
return 0;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public int ForEachValue(MyAction<TValue> func)
|
|||
|
{
|
|||
|
int count = 0;
|
|||
|
if (_tank != null)
|
|||
|
{
|
|||
|
var e = _tank.GetEnumerator();
|
|||
|
try
|
|||
|
{
|
|||
|
while (e.MoveNext())
|
|||
|
{
|
|||
|
func(e.Current.Value.value);
|
|||
|
++count;
|
|||
|
}
|
|||
|
}
|
|||
|
finally
|
|||
|
{
|
|||
|
e.Dispose();
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
return count;
|
|||
|
}
|
|||
|
|
|||
|
public int ForEach<TCtx>(MyAction<TKey, TValue, TCtx> func, TCtx ctx = null) where TCtx : class
|
|||
|
{
|
|||
|
int count = 0;
|
|||
|
if (_tank != null)
|
|||
|
{
|
|||
|
var e = _tank.GetEnumerator();
|
|||
|
try
|
|||
|
{
|
|||
|
while (e.MoveNext())
|
|||
|
{
|
|||
|
func(e.Current.Key, e.Current.Value.value, ctx);
|
|||
|
++count;
|
|||
|
}
|
|||
|
}
|
|||
|
finally
|
|||
|
{
|
|||
|
e.Dispose();
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
return count;
|
|||
|
}
|
|||
|
|
|||
|
public int Clear(MyFunc<int, int, int, bool> func)
|
|||
|
{
|
|||
|
int result = 0;
|
|||
|
if (_tank != null)
|
|||
|
{
|
|||
|
List<TKey> _removeList = null;
|
|||
|
var e = _tank.GetEnumerator();
|
|||
|
try
|
|||
|
{
|
|||
|
while (e.MoveNext())
|
|||
|
{
|
|||
|
var item = e.Current;
|
|||
|
bool skip = func(item.Value.generation, item.Value.lastTouchedTime, item.Value.livedTime);
|
|||
|
if (!skip)
|
|||
|
{
|
|||
|
if (_removeList == null)
|
|||
|
{
|
|||
|
_removeList = new List<TKey>();
|
|||
|
}
|
|||
|
_ReleaseValue(item.Value.value);
|
|||
|
_removeList.Add(item.Key);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
finally
|
|||
|
{
|
|||
|
e.Dispose();
|
|||
|
}
|
|||
|
|
|||
|
if (_removeList != null)
|
|||
|
{
|
|||
|
for (int i = 0; i < _removeList.Count; ++i)
|
|||
|
{
|
|||
|
if (_tank.Remove(_removeList[i]))
|
|||
|
{
|
|||
|
result += 1;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
return result;
|
|||
|
}
|
|||
|
|
|||
|
public int Sweep(bool force = false)
|
|||
|
{
|
|||
|
int result = 0;
|
|||
|
if (force || _sweeper.Sweepable())
|
|||
|
{
|
|||
|
result = _DoSweep(force);
|
|||
|
}
|
|||
|
return result;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public class DataCacheLit<TKey, TValue> : DataCache<TKey, object>
|
|||
|
{
|
|||
|
public bool Add(TKey key, TValue value)
|
|||
|
{
|
|||
|
return base.Add(key, value);
|
|||
|
}
|
|||
|
public bool TryGetValue(TKey key, out TValue value, bool sweep = true)
|
|||
|
{
|
|||
|
object _value = null;
|
|||
|
value = default(TValue);
|
|||
|
var r = base.TryGetValue(key, out _value, sweep);
|
|||
|
if (r)
|
|||
|
{
|
|||
|
try
|
|||
|
{
|
|||
|
value = (TValue)_value;
|
|||
|
}
|
|||
|
catch (Exception e)
|
|||
|
{
|
|||
|
System.Diagnostics.Trace.Fail(e.Message,e.StackTrace);
|
|||
|
}
|
|||
|
}
|
|||
|
return r;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|