背景
在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 = 10) { 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 = 0; cache.ar.Set(); } } } } }
问题
CacheContainer中的变量,均是在线程池里有个线程去检测它,也就是说有10个变量,线程池里就会多10个子线程,这样会不会不太好。
该方法仅是抛砖引玉,不知道在Winform中实现缓存变量xx分钟这样的功能有没有更好的方法,欢迎赐教,谢谢!