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

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

 

 

 

前情回顾

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

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

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

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

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

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

 

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

 

 

 

实现设计

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

 

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

 

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

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

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

 

 

 

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

 

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

         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();
        }

 

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

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

 

 

 

 

关于枚举器并发访问

相比循环队列中我们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;

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

一旦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的时候 完全可以脏读————没有人在意自己读取的是最上面一条信息 还是第二个信息 他们只关心是否能读下去

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

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

 

Code
namespace WayneGameSolution.Chat
{

    
public class ChatMessage : IChatMessage, IRecycleNode<IChatMessage> 
    {

        
public ChatMessage()
        { }

 

        
#region IChatMessage Members
        
        
public string ChannelID
        {
            
get;
            
set;
        }
        
        
public string ChannelName
        {
            
get;
            
set;
        }
        
        
public string ChaterFrom
        {
            
get;
            
set
        }
        
        
public string ChaterTo
        {
            
get;
            
set
        }
        
        
public MessageRenderType RenderType
        {
            
get;
            
set;
        }
        
        
public string Text
        {
            
get;
            
set;
        }
        
        
public DateTime TimeStamp
        {
            
get;
            
set;
        }

   
        
#endregion

        
#region ICloneable Members

        
public object Clone()
        {
            
return MemberwiseClone();
        }

        
#endregion



        
#region IRecycleNode<IChatMessage> Members

        
public IRecycleNode<IChatMessage> Lower
        {
            
get;
            
set;
        }

        
public IRecycleNode<IChatMessage> Higher
        {
            
get;
            
set;
        }

        
public IChatMessage Value
        {
            
get
            {
                
return this;
            }
            
set
            {
                
throw new NotImplementedException();
            }
        }

        
#endregion
    }
}

 

垃圾列表最终定稿

 

RecycleList
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.Threading;
using System.Security.Permissions;



namespace System.Collections.Generic.Sync
{
    
public class RecycleList<T> : IEnumerable<T>
    {
        System.Threading.ReaderWriterLockSlim _lock 
= new ReaderWriterLockSlim();
        IRecycleNode
<T> _Bottom, _Top;
        
//public IRecycleNode<T> Top
        
//{
        
//    get
        
//    {
        
//        _lock.EnterReadLock();
        
//        var t = _Top;
        
//        _lock.ExitReadLock();
        
//        return t;
 
        
//    }
        
//}

        
public void Clear()
        {
            _lock.EnterWriteLock();
            _Top 
= null;
            _lock.ExitWriteLock();
        }

        
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();
        }

        
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;

            }
            
        }

        
#region IEnumerable<T> Members
        
public IEnumerator<T> GetEnumerator()
        {
            
return new RecycleListEnumerator<T>(_Top);
        }

        
#endregion
    
        
#region IEnumerable Members

        IEnumerator IEnumerable.GetEnumerator()
        {
            
return new RecycleListEnumerator<T>(_Top);
        }

        
#endregion


    
    }

    
public interface IRecycleNode<T>
    {
        IRecycleNode
<T> Lower { getset; }
        IRecycleNode
<T> Higher { getset; }
        T Value { 
getset; }
        DateTime TimeStamp
        {
            
get;
            
set;
        }
    }

    
public class RecycleListEnumerator<T> : IEnumerator<T>
    {
        IRecycleNode
<T> _beginNode;
        IRecycleNode
<T> _currentNode;
     
public      RecycleListEnumerator(IRecycleNode<T> beginNode)
        {
            _beginNode 
= _currentNode = beginNode;
        }
        
#region IEnumerator<T> Members

        
public T Current
        {
            
get { return _currentNode.Value; }
        }

        
#endregion

        
#region IDisposable Members

        
public void Dispose()
        {
            _beginNode 
= null;
            _currentNode 
= null;
        }
        
#endregion

        
#region IEnumerator Members

        
object IEnumerator.Current
        {
            
get { return Current; }
        }
        
private bool startedFlag=false ;
        
public bool MoveNext()
        {
            
if (!startedFlag) 
            {
                startedFlag 
=true ;
                
return (_beginNode != null);

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

        
public void Reset()
        {
            _currentNode 
= _beginNode;
        }

        
#endregion
    }

}

 

 

 

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

 

 

下载

 

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

wgs-game-lobby.googlecode.com

 

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

 

连接:

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

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

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

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

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

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

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

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

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

你可能感兴趣的:(游戏大厅 从基础开始(8)--绕回来细说聊天室(下)垃圾列表)