我想实现HashTable
(或mabybe a HashSet
或Dictionary
),其中包含一段时间后过期的唯一成员。例如:
// Items expire automatically after 10 seconds (Expiration period = 10 sec)
bool result = false;
// Starting from second 0
result = MyHashSet.Add("Bob"); // second 0 => true
result = MyHashSet.Add("Alice"); // second 5 => true
result = MyHashSet.Add("Bob"); // second 8 => false (item already exist)
result = MyHashSet.Add("Bob"); // second 12 => true (Bob has expired)
如何以最低成本以线程安全的方式做到这一点?
答案 0 :(得分:8)
您可以创建自己的哈希表,其中每个项目包含创建时间和时间跨度。 在您尝试返回值的索引器中,如果项的生命周期已过期,则返回null。并删除该项目。从表中删除项目的后台线程将无法确保您不会在没有此项的情况下返回过期项目。然后你可以创建一个执行此操作的线程,只是为了最大限度地删除过期的项目,以便在很多项目永远不会被访问时最小化内存消耗。
答案 1 :(得分:7)
您是否考虑过使用System.Web.Caching
而不是自己动手?
http://www.hanselman.com/blog/UsingTheASPNETCacheOutsideOfASPNET.aspx
修改强>
以上情况不应该给系统增加很多开销,但要看看这个。
以下代码中的一些健康警告。
throw new NotImplementedException()
。我会试着回过头来,因为这是一个有趣的难题。IDictionary<TKey, TValue>, ICollection<KeyValuePair<TKey, TValue>>, IEnumerable<KeyValuePair<TKey, TValue>>, IDictionary, ICollection, IEnumerable, ISerializable, IDeserializationCallback
测试代码
TimeSpan t = new TimeSpan(0,0,5); //5 Second Expiry
ExpiringDictionary<int, string> dictionary
= new ExpiringDictionary<int,string>(t);
dictionary.Add(1, "Alice");
dictionary.Add(2, "Bob");
dictionary.Add(3, "Charlie");
//dictionary.Add(1, "Alice"); //<<this will throw a exception as normal...
System.Threading.Thread.Sleep(6000);
dictionary.Add(1, "Alice"); //<< this however should work fine as 6 seconds have passed
<强>实施强>
public class ExpiringDictionary<TKey, TValue> : IDictionary<TKey, TValue>
{
private class ExpiringValueHolder<T> {
public T Value { get; set; }
public DateTime Expiry { get; private set; }
public ExpiringValueHolder(T value, TimeSpan expiresAfter)
{
Value = value;
Expiry = DateTime.Now.Add(expiresAfter);
}
public override string ToString() { return Value.ToString(); }
public override int GetHashCode() { return Value.GetHashCode(); }
};
private Dictionary<TKey, ExpiringValueHolder<TValue>> innerDictionary;
private TimeSpan expiryTimeSpan;
private void DestoryExpiredItems(TKey key)
{
if (innerDictionary.ContainsKey(key))
{
var value = innerDictionary[key];
if (value.Expiry < System.DateTime.Now)
{
//Expired, nuke it in the background and continue
innerDictionary.Remove(key);
}
}
}
public ExpiringDictionary(TimeSpan expiresAfter)
{
expiryTimeSpan = expiresAfter;
innerDictionary = new Dictionary<TKey, ExpiringValueHolder<TValue>>();
}
public void Add(TKey key, TValue value)
{
DestoryExpiredItems(key);
innerDictionary.Add(key, new ExpiringValueHolder<TValue>(value, expiryTimeSpan));
}
public bool ContainsKey(TKey key)
{
DestoryExpiredItems(key);
return innerDictionary.ContainsKey(key);
}
public bool Remove(TKey key)
{
DestoryExpiredItems(key);
return innerDictionary.Remove(key);
}
public ICollection<TKey> Keys
{
get { return innerDictionary.Keys; }
}
public bool TryGetValue(TKey key, out TValue value)
{
bool returnval = false;
DestoryExpiredItems(key);
if (innerDictionary.ContainsKey(key))
{
value = innerDictionary[key].Value;
returnval = true;
} else { value = default(TValue);}
return returnval;
}
public ICollection<TValue> Values
{
get { return innerDictionary.Values.Select(vals => vals.Value).ToList(); }
}
public TValue this[TKey key]
{
get
{
DestoryExpiredItems(key);
return innerDictionary[key].Value;
}
set
{
DestoryExpiredItems(key);
innerDictionary[key] = new ExpiringValueHolder<TValue>(value, expiryTimeSpan);
}
}
public void Add(KeyValuePair<TKey, TValue> item)
{
DestoryExpiredItems(item.Key);
innerDictionary.Add(item.Key, new ExpiringValueHolder<TValue>(item.Value, expiryTimeSpan));
}
public void Clear()
{
innerDictionary.Clear();
}
public int Count
{
get { return innerDictionary.Count; }
}
public bool IsReadOnly
{
get { return false; }
}
public bool Contains(KeyValuePair<TKey, TValue> item)
{
throw new NotImplementedException();
}
public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
{
throw new NotImplementedException();
}
public bool Remove(KeyValuePair<TKey, TValue> item)
{
throw new NotImplementedException();
}
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
{
throw new NotImplementedException();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
throw new NotImplementedException();
}
}
答案 2 :(得分:0)
试试这个:
static void Main() {
ExpirationList<string> list = new ExpirationList<string>(new List<string>());
bool r1 = list.Add("Bob", 3000); // true
Thread.Sleep(2000);
bool r2 = list.Add("Bob", 3000); // false
Thread.Sleep(2000);
bool r3 = list.Add("Bob", 3000); // true
}
public class ExpirationList<T> {
private List<T> _list;
public ExpirationList(List<T> list) {
if (list == null) throw new ArgumentException();
_list = list;
}
public bool Add(T item, int lifetime) {
lock (_list) {
if (_list.Contains(item))
return false;
_list.Add(item);
}
new Action<int>(time => Thread.Sleep(time))
.BeginInvoke(lifetime, new AsyncCallback(result => {
T obj = (T)result.AsyncState;
lock (_list) {
_list.Remove(obj);
}
}), item);
return true;
}
// add other proxy code here
}
当然,List可以替换为Hashtable和(它会更加正确)异步委托可以替换为Timers,但我希望常见的方法是明确的