背景

  在Web中Session的功能很好用,于是想Winform中实现该功能,典型应用场景则是登陆成功后,当一段时间不操作,则该会话过期,提示重新登陆。

资源下载

  测试代码

  示例说明:登陆进去10s不操作或者访问Cache后10秒不操作,则会提示登陆超时

实现

  1. 设计CacheContainer类,使用Dictionary存放变量,并添加互斥锁SyncRoot,避免多线程操作带来异常。

  2. CacheContainer内部的变量,如果持续10秒(测试使用的默认值)没有访问或操作,则自动移除该变量,并触发回调。

  3. 当程序访问CacheContainer内部的变量,过期时间从当前时间开始计时。

  4. 变量第一次计时在加入CacheContainer时。

  具体代码如下:

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading; namespace WindowsFormsApplication1
{
public sealed class CacheContainer
{
//互斥锁
private object SyncRoot = new object();
//对象字典
private Dictionary<string, CacheObj> dic = new Dictionary<string, CacheObj>(); internal class CacheObj : IDisposable
{
//公有变量
public object Value { get; set; }
public int Expired { get; set; }
public WaitCallback callback { get; set; }
public AutoResetEvent ar = new AutoResetEvent(false);
public TimeSpan Timeout
{
get
{
return TimeSpan.FromSeconds(Expired);
}
}
public CacheObj(object value, int expired = )
{
Value = value;
Expired = expired;
callback = new WaitCallback((obj) =>
{
Console.WriteLine("{0}:已过期,过期时间:{1}", obj, DateTime.Now);
});
} public void Dispose()
{
GC.SuppressFinalize(this);
} ~CacheObj()
{
Dispose();
}
} private static CacheContainer container = new CacheContainer();
private CacheContainer() { }
public static CacheContainer GetInstance() { return container; } public object this[string key]
{
get
{
//访问变量成功,则时间重新计时
CacheObj cache;
lock (SyncRoot)
{
if (dic.TryGetValue(key, out cache))
{
cache.ar.Set();
return cache.Value;
}
}
return null;
}
set
{
//通过属性添加参数,有则覆盖,无则添加,操作完毕重新计时
CacheObj cache;
lock (SyncRoot)
{
if (dic.TryGetValue(key, out cache))
{
cache.Value = value;
cache.ar.Set();
}
else
Add(key, value);
}
}
} public void Add(string key, object value)
{
lock (SyncRoot)
dic.Add(key, new CacheObj(value));
AutoCheck(key);
} public void Add(string key, object value, int expired)
{
lock (SyncRoot)
dic.Add(key, new CacheObj(value, expired));
AutoCheck(key);
} public void Add(string key, object value, int expired, WaitCallback callback)
{
lock (SyncRoot)
dic.Add(key, new CacheObj(value, expired) { callback = callback });
AutoCheck(key);
} private void AutoCheck(string key)
{
//开启一个子线程去控制变量的过期
ThreadPool.QueueUserWorkItem(new WaitCallback((obj) =>
{
CacheObj tmpCache;
while (true)
{
//从字典中取出对象
lock (SyncRoot)
tmpCache = dic[key];
//打印变量过期时间
Console.WriteLine("{0} 等待销毁变量 时间为:{1}秒", DateTime.Now, tmpCache.Expired);
//记录开始时间
var timeStart = DateTime.Now;
//中断,超时时间一到,自动向下执行
tmpCache.ar.WaitOne(TimeSpan.FromSeconds(tmpCache.Expired));
//检查时间是否已经达到超时时间,超时则移除该信息,并触发回调
if ((DateTime.Now - timeStart) >= tmpCache.Timeout)
{
lock (SyncRoot)
dic.Remove(key);
if (tmpCache.callback != null) tmpCache.callback(tmpCache.Value);
break;
}
}
}));
} public void Remove(string key)
{
lock (SyncRoot)
{
CacheObj cache;
if (dic.TryGetValue(key, out cache))
{
cache.Expired = ;
cache.ar.Set();
}
}
}
}
}

问题  

  CacheContainer中的变量,均是在线程池里有个线程去检测它,也就是说有10个变量,线程池里就会多10个子线程,这样会不会不太好。

  该方法仅是抛砖引玉,不知道在Winform中实现缓存变量xx分钟这样的功能有没有更好的方法,欢迎赐教,谢谢!

05-11 10:57
查看更多