• 游戏大厅 从基础开始(8)绕回来细说聊天室(下)垃圾列表


    没错,这次章节没有女仆。

    前情回顾

    我们的最初的需求是建立一个拉模式下用户暂存的顺序信息池

    还是这张工作模式图 我们可以把这个需求设计为

    • Clear:清除所有内容
    • GetEnumerator :实现枚举器,新向旧方向的顺序枚举,这样一旦到达上次读取的时间就可以中断枚举。
    • RecycleFromButtom:从旧向前进行搜索 把满足条件的扔到GC
    • StackOn :把一个新信息放在堆栈的顶部

    这就好像是一个旧报纸回收传送带,一群人在焚烧之前看看还有没有什么值得保存的信息 排个照,存个档,没有用的就直接扔进焚化炉。

    实现设计

    根据上一章的研究结果 我们需要一个能够在写的同时 能够完全无锁并发访问的顺序数据池

    看起来基于 Array的任何数据结构都不太适合并发读。

    这时候我们把目光转向链表结构。

    链表在实现聊天室时得天独厚

    姑且我们为垃圾列表建立这样一个链表节点

            public void StackOn( IRecycleNode<T>  newOne)
            {
                _lock.EnterWriteLock ();
                newOne.Lower 
    = newOne.Higher = null;
                
    if (_Top != null)
                {
                    _Top.Higher 
    = newOne;
                    newOne.Lower 
    = _Top;
                    _Top 
    = newOne;
              
                }
                
    else
                {                
                     _Top 
    =newOne  ;
                     _Bottom 
    = _Top;
                }
                newOne.TimeStamp 
    = DateTime.Now;
                _lock.ExitWriteLock();
            }

    我们以此作为基础继续讨论。

    关于枚举器并发访问

    相比循环队列中我们MoveNext的时候需要的 _size, _head, _index 和array中元素 这几个变量随时可能产生的并发冲突

    public bool MoveNext()
    {
        
    if (this._version != this._q._version)
        {
            ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumFailedVersion);
        }
        
    if (this._index == -2)
        {
            
    return false;
        }
        
    this._index++;
        
    if (this._index == this._q._size)
        {
            
    this._index = -2;
            
    this._currentElement = default(T);
            
    return false;
        }
        
    this._currentElement = this._q.GetElement(this._index);
        
    return true;
    }

    Queue:

    internal T GetElement(int i)

           return this._array[(this._head + i) % this._array.Length];
    }

    在MoveNext的时候 我们的RecycleList访问的是一个不会脏的引用 :Lower

            public bool MoveNext()
            {
                
    if (!startedFlag) 
                {
                    startedFlag 
    =true ;
                    
    return (_beginNode != null);

                };
                var cl 
    = _currentNode.Lower;
                
    if (cl != null)
                {
                    _currentNode 
    = cl;
                    
    return true;
                }
                
    else
                    
    return false;
            }

    不冲突 就是不冲突~

    关于并发回收

    链表回收相当的简单, node. Lower=null;

    一旦node的lower设置为 null 那么下面的所有节点就脱离了GCRoot 就可以被回收了

    如果别的线程正在其下的_Bottom检查回收, 由于 Higher的联系仍然没断,向上的枚举仍然可以进行

      public void RecycleFromButtom(Func<IRecycleNode<T>bool> RecycleCondition)
            {
                var bn 
    = _Bottom;
                
    //if (_Bottom == null) _Bottom = ;
                var bh = bn;
                
    if (bh != null) bh = bh.Higher;
                
    while (bn != null & bh != null)
                {
                    
    if (RecycleCondition(bn))
                    {                
                            bh.Lower 
    = null;
                    }
                    _Bottom 
    = bh;
                    bn 
    = _Bottom;
                    bh 
    = bn.Higher;

                }
                
            }

    _Bottom 决定了GC能够回收的最后一个节点。 它不需要安全。 就算它被指定为 实际底部下面的其他节点 也仅仅使一两个节点苟延残喘一个周期。

    唯一的迷你锁:StackOn

    理论上的完全无锁 被破坏在写入这一关。

    由于在链表头部增加节点并不是原子操作,所以这里必须增加一个写入锁

    这个锁非常非常的小 :

            public void StackOn( IRecycleNode<T>  newOne)
            {
                _lock.EnterWriteLock ();
                newOne.Lower 
    = newOne.Higher = null;
                
    if (_Top != null)
                {
                    _Top.Higher 
    = newOne;
                    newOne.Lower 
    = _Top;
                    _Top 
    = newOne;
              
                }
                
    else
                {                
                     _Top 
    =newOne  ;
                     _Bottom 
    = _Top;
                }
                newOne.TimeStamp 
    = DateTime.Now;
                _lock.ExitWriteLock();
            }

    由于 对_top的修改是原子性的

    _Top.Higher = newOne;

    newOne.Lower = _Top;

    _Top = newOne;

    在创建Enumertor的时候 完全可以脏读————没有人在意自己读取的是最上面一条信息 还是第二个信息 他们只关心是否能读下去

    只要我们的ChatMessage 实现 IRecycleNode,我们就可以把它放进这个池中了

    Code

    垃圾列表最终定稿

    RecycleList

    这里提供一个 MVC的测试聊天室  包括所有源代码

    下载

    大厅源代码正在googlecode 上用c#缓慢重建   有兴趣的可以下载

    wgs-game-lobby.googlecode.com

     聊天室

    连接:

    游戏大厅 从基础开始(1)——最简单的关系,用户与房间

    游戏大厅 从基础开始(2)——最基础的交流:聊天

    游戏大厅 从基础开始(3)——最吸引眼球的部分 客户端与服务器的连接

    游戏大厅 从基础开始(3.5)——最吸引眼球的部分 客户端与服务器的连接 的实现  

    游戏大厅 从基础开始(4)-通过L2X用配置文件反射组装程序(VB only)

    游戏大厅 从基础开始(5)--绕回来细说聊天室(上)

    游戏大厅 从基础开始(6)--绕回来细说聊天室(中)之女仆编年史1

    游戏大厅 从基础开始(7)--绕回来细说聊天室(中间偏下)之女仆编年史2 

    游戏大厅 从基础开始(8)--绕回来细说聊天室(下)垃圾列表

  • 相关阅读:
    jsonp跨域
    angular总结控制器的三种主要职责: 为应用中的模型设置初始状态 通过$scope对象把数据模型或函数行为暴露给视图 监视模型的变化,做出相应的动作
    url解析
    waterfall.js
    ES6
    前端代码规范
    秒杀倒计时功能实现
    怎样正确写网站title、keywords、description比较标准。
    CSS3动画
    Python3基础 父,子类普通方法重名 子类方法覆盖父类方法
  • 原文地址:https://www.cnblogs.com/waynebaby/p/1555034.html
Copyright © 2020-2023  润新知