C#中通过LRU实现通用高效的超时连接探测

编写网络通讯都要面对一个问题,就是要把很久不存活的死连接清除,如果不这样做那死连接最终会占用大量内存影响服务运作!在实现过程中一般都会使用ping,pong原理,通过ping,pong来更新连接的时效性,最后通过扫描连接列表来清除掉。虽然这种做法比较简单,但很难抽取出通用性的封装,扫描整个列表复杂度也比较高。以下讲解如何通过LRU算法实现一个通用高效的探测超时连接功能类。

什么是LRU

在这里还是要大概介绍一下LRU,LRU算法的设计原则是:如果一个数据在最近一段时间没有被访问到,那么在将来它被访问的可能性也很小.也就是说,当限定的空间已存满数据时,应当把最久没有被访问到的数据淘汰.当然在这里并不需要使用到自动淘汰机制,只需要把未位到达超时的连接清除即可。

在C#中如何实现LRU

C#并不存在这样的数据结构,不过有一个结构很适合实现LRU,这个结构就是LinkedList双向链表,通过以下结构图就容易理解通过LinkedList实现LRU

通过LinkedList的功能我们可以把活越项先移出来,然后再把项移到头部。在这里需要注意LinkedList的Remove方法,它有两个重载版本,两个版本的复杂度不一样。一个是O(n)一个是O(1)所以使用上一定要注意,否则在数据多的情况下效率差别巨大(这些细节都可以通过源代码来查看)!

代码实现

前面已经大概讲述的原理,接下来要做的就是代码实现了。第一步需要制订一个基础可控测对象规则接口,这样就可以让现有的已经实现的功能实现它并可得到相关功能的支持。

public interface IDetector
  {
    double ActiveTime
    { get; set; }
    LinkedListNode<IDetector> DetectorNode
    {
      get;
      set;
    }
  }

接口定义了两个属性,一个是最近活越时间,另一个就是LinkedListNode<IDetector>这个属性比交关键,通过LinkedListNode<IDetector>可以让LinkedList在Remove时复杂度为O(1).接下来就要针对基于LRU算法处理超时制定一个应用规则

 public interface ILRUDetector
  {
    void Update(IDetector item);
    void Detection(int timeout);
    double GetTime();
    Action<IList<IDetector>> Timeout { get; set; }
  }

规则也是比较简单,Update用于更新跟踪对象,一般在处理接受ping或pong包后进行调用;Detection方法是探测超出指定时间的对象,时间当位是毫秒,如果存在有超时的对象则触发Timeout事件;GetTime是获取探测器已经运行的时间单位毫秒!规则定好了那接着要做的事实就是要实现它:

 class LRUDetector : ILRUDetector, IDisposable
  {
    public LRUDetector()
    {
      mTimeWatch = new System.Diagnostics.Stopwatch();
      mTimeWatch.Restart();
    }
    private Buffers.XSpinLock xSpinLock = new Buffers.XSpinLock();
    private System.Diagnostics.Stopwatch mTimeWatch;
    private LinkedList<IDetector> mItems = new LinkedList<IDetector>();
    public Action<IList<IDetector>> Timeout
    {
      get; set;
    }
    public void Detection(int timeout)
    {
      double time = GetTime();
      List<IDetector> result = new List<IDetector>();
      using (xSpinLock.Enter())
      {
        LinkedListNode<IDetector> last = mItems.Last;
        while (last != null && (time - last.Value.ActiveTime) > timeout)
        {
          mItems.Remove(last);
          result.Add(last.Value);
          last.Value.DetectorNode = null;
          last = mItems.Last;
        }
      }
      if (Timeout != null && result.Count > 0)
        Timeout(result);
    }
    public void Update(IDetector item)
    {
      using (xSpinLock.Enter())
      {
        if (item.DetectorNode == null)
          item.DetectorNode = new LinkedListNode<IDetector>(item);
        item.ActiveTime = GetTime();
        if (item.DetectorNode.List == mItems)
          mItems.Remove(item.DetectorNode);
        mItems.AddFirst(item);
      }
    }
    public void Dispose()
    {
      mItems.Clear();
    }
    public double GetTime()
    {
      return mTimeWatch.Elapsed.TotalMilliseconds;
    }
  }

代码并不复杂,相信不用过多解释也能看懂相关操作原理。

测试

既然功能已经实现,接下来就要对代码进行测试看运行效果。测试代码比较简单首先开启一个Timer定时执行Detection,另外开一个线程去调用Update方法

class Program
  {
    public class TestDetector : IDetector
    {
      public double ActiveTime { get; set; }
      public string Name { get; set; }
      public LinkedListNode<IDetector> DetectorNode { get; set; }
    }
    static void Main(string[] args)
    {
      LRUDetector lRUDetector = new LRUDetector();
      lRUDetector.Timeout = (items) =>
      {
        foreach (TestDetector item in items)
          Console.WriteLine($"{(item.Name)} timeout {lRUDetector.GetTime() - item.ActiveTime}ms");
      };
      System.Threading.Timer timer = null;
      timer = new System.Threading.Timer(o =>
      {
        timer.Change(-1, -1);
        lRUDetector.Detection(5000);
        timer.Change(5000, 5000);
      }, null, 5000, 5000);
      System.Threading.ThreadPool.QueueUserWorkItem(o =>
      {
        int i = 0;
        while (true)
        {
          System.Threading.Thread.Sleep(500);
          i++;
          TestDetector testDetector = new TestDetector();
          testDetector.Name = "my name is " + i;
          lRUDetector.Update(testDetector);
        }
      });
      Console.Read();
    }
  }

运行效果:

以上所述是小编给大家介绍的C#中通过LRU实现通用高效的超时连接探测,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对我们网站的支持!

(0)

相关推荐

  • C#中通过LRU实现通用高效的超时连接探测

    编写网络通讯都要面对一个问题,就是要把很久不存活的死连接清除,如果不这样做那死连接最终会占用大量内存影响服务运作!在实现过程中一般都会使用ping,pong原理,通过ping,pong来更新连接的时效性,最后通过扫描连接列表来清除掉.虽然这种做法比较简单,但很难抽取出通用性的封装,扫描整个列表复杂度也比较高.以下讲解如何通过LRU算法实现一个通用高效的探测超时连接功能类. 什么是LRU 在这里还是要大概介绍一下LRU,LRU算法的设计原则是:如果一个数据在最近一段时间没有被访问到,那么在将来它被

  • java中List对象排序通用方法

    本文实例讲述了java中List对象排序通用方法.分享给大家供大家参考.具体分析如下: 在数据库中查出来的列表list中,往往需要对不同的字段重新排序,一般的做法都是使用排序的字段,重新到数据库中查询.如果不到数据库查询,直接在第一次查出来的list中排序,无疑会提高系统的性能. 只要把第一次查出来的结果存放在session中,就可以对list重新排序了.一般对list排序可以使用Collections.sort(list),但如果list中包含是一个对象的话,这种方法还是行不通的.那要怎么排序

  • 基于Java子线程中的异常处理方法(通用)

    在普通的单线程程序中,捕获异常只需要通过try ... catch ... finally ...代码块就可以了.那么,在并发情况下,比如在父线程中启动了子线程,如何在父线程中捕获来自子线程的异常,从而进行相应的处理呢? 常见错误 也许有人会觉得,很简单嘛,直接在父线程启动子线程的地方try ... catch一把就可以了,其实这是不对的. 原因分析 让我们回忆一下Runnable接口的run方法的完整签名,因为没有标识throws语句,所以方法是不会抛出checked异常的.至于Runtime

  • 在django中,关于session的通用设置方法

    最近发现session的知识有点脱节了,默认设置愣是搞半天,看来忘了不少.今天把一些通用设置贴上来,以备随时回顾. 配置文件中设置默认操作(通用配置): SESSION_COOKIE_NAME = "sessionid" # Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串(默认) SESSION_COOKIE_PATH = "/" # Session的cookie保存的路径(默认) SESSION_COOKIE_DOMAIN

  • Redis中键和数据库通用指令详解

    目录 一.Redis键(key)通用指令 1.key基本操作 2.时效性控制 3.查询模式 4.其它操作 二.数据库通用指令 1.基本操作 2.相关操作 一.Redis键(key)通用指令 可以参考菜鸟教程:Redis 键命令用于管理 redis 的键 key特征:key是一个字符串,通过key获取redis中保存的数据. 1.key基本操作 命令 功能 del key 该命令用于在 key 存在时删除 key exists key 检查给定 key 是否存在 type key 返回 key 所

  • 高效管理http连接的方法

    1.Http连接基础 Http协议承载了互联网上的主要流量,然而说到传输,还要回归到最基本的网络分层模型TCP/IP.TCP/IP是全球计算机及网络设备都在使用的一种常用的分组交互网络分层协议集.客户端可以打开一条TCP/IP连接,与世界上的任何服务器进行数据交换,并且交换的数据永远不会丢失,受损或失序. 下面是常见的TCP/IP分层协议,分为安全与非安全版本. 由图可知,HTTP的整个传输过程可以描述为"HTTP over TCP over IP".TCP是可靠地传输协议,就好像一条

  • JS中数组实现代码(倒序遍历数组,数组连接字符串)

    Js中的数组是一种特殊的对象,用来表示偏移量的索引是该对象的属性. 具体代码如下所示: // =================== 求最大值===================================== <script> var arr = [10,35,765,21345,678,89]; var max = arr [0]; for (var i=0;i< arr.length;i++) { if (max<arr[i]){ max = arr [i]; } }

  • Redis中Lua脚本的使用和设置超时

    目录 EVAL命令简介 eval格式 特性 执行流程 关于脚本超时 SCRIPT KILL 命令 SHUTDOWN NOSAVE 命令 参考 Redis提供了Lua脚本功能来让用户实现自己的原子命令,但也存在着风险,编写不当的脚本可能阻塞线程导致整个Redis服务不可用. 本文将介绍Redis中Lua脚本的基本用法,以及脚本超时导致的问题和处理方式. EVAL命令简介 eval格式 Redis 提供了命令EVAL来执行Lua脚本,格式如下 EVAL script numkeys key [key

  • Spring MVC处理参数中的枚举类型通用实现方法

    前言 在开发的过程中,会涉及到大量的枚举类型数据,我们都知道,Springmvc本身能自动转换很多的数据类型,也支持你自定义转换类型,非常灵活. 本文主要介绍的是关于Spring MVC处理参数的枚举类型通用实现的相关内容,下面话不多说了,来一起看看详细的介绍吧 业务场景: 前端提交了枚举的一个属性value,想由spring来完成参数类型自动转换成对应的枚举. 比方有一个枚举 @AllArgsConstructor(access = AccessLevel.PRIVATE) @Getter @

  • js中setTimeout的妙用--防止循环超时

    上个周日,介绍了如何使用setTimeout代替setInterval进行间歇调用,这个周日,继续来讲<JavaScript高级程序设计>这本书里面,对于setTimeout的另一种妙用--防止循环超时  [这是铺垫,为故事的高潮埋下伏笔] JS是单线程的,一个代码块里面的代码,只能按顺序从上到下执行,所以如果中间有一块代码,执行起来非常耗时,就会导致下面的代码无法执行,出现浏览器假死的状态. JS的耗时操作,常见的有两种  1.向服务器发起请求   2.对数组的循环操作  (当然,还有一种,

随机推荐