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

posted @ 2009-08-27 14:06  MSFT:waywa 韦恩卑鄙  阅读(2085)  评论(2编辑  收藏  举报