zoukankan      html  css  js  c++  java
  • 游戏大厅 从基础开始(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)--绕回来细说聊天室(下)垃圾列表

  • 相关阅读:
    【leetcode】1295. Find Numbers with Even Number of Digits
    【leetcode】427. Construct Quad Tree
    【leetcode】1240. Tiling a Rectangle with the Fewest Squares
    【leetcode】1292. Maximum Side Length of a Square with Sum Less than or Equal to Threshold
    【leetcode】1291. Sequential Digits
    【leetcode】1290. Convert Binary Number in a Linked List to Integer
    【leetcode】1269. Number of Ways to Stay in the Same Place After Some Steps
    【leetcode】1289. Minimum Falling Path Sum II
    【leetcode】1288. Remove Covered Intervals
    【leetcode】1287. Element Appearing More Than 25% In Sorted Array
  • 原文地址:https://www.cnblogs.com/waynebaby/p/1555034.html
Copyright © 2011-2022 走看看