没错,这次章节没有女仆。
前情回顾
我们的最初的需求是建立一个拉模式下用户暂存的顺序信息池
还是这张工作模式图 我们可以把这个需求设计为
- 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
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 { get; set; }
IRecycleNode<T> Higher { get; set; }
T Value { get; set; }
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
连接:
游戏大厅 从基础开始(1)——最简单的关系,用户与房间
游戏大厅 从基础开始(2)——最基础的交流:聊天
游戏大厅 从基础开始(3)——最吸引眼球的部分 客户端与服务器的连接
游戏大厅 从基础开始(3.5)——最吸引眼球的部分 客户端与服务器的连接 的实现
游戏大厅 从基础开始(4)-通过L2X用配置文件反射组装程序(VB only)
游戏大厅 从基础开始(5)--绕回来细说聊天室(上)
游戏大厅 从基础开始(6)--绕回来细说聊天室(中)之女仆编年史1
游戏大厅 从基础开始(7)--绕回来细说聊天室(中间偏下)之女仆编年史2
游戏大厅 从基础开始(8)--绕回来细说聊天室(下)垃圾列表