没错,这次章节没有女仆。
我们的最初的需求是建立一个拉模式下用户暂存的顺序信息池
还是这张工作模式图 我们可以把这个需求设计为
- Clear:清除所有内容
- GetEnumerator :实现枚举器,新向旧方向的顺序枚举,这样一旦到达上次读取的时间就可以中断枚举。
- RecycleFromButtom:从旧向前进行搜索 把满足条件的扔到GC
- StackOn :把一个新信息放在堆栈的顶部
这就好像是一个旧报纸回收传送带,一群人在焚烧之前看看还有没有什么值得保存的信息 排个照,存个档,没有用的就直接扔进焚化炉。
实现设计
根据上一章的研究结果 我们需要一个能够在写的同时 能够完全无锁并发访问的顺序数据池
看起来基于 Array的任何数据结构都不太适合并发读。
这时候我们把目光转向链表结构。
链表在实现聊天室时得天独厚
姑且我们为垃圾列表建立这样一个链表节点
{
_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中元素 这几个变量随时可能产生的并发冲突
{
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
{
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的联系仍然没断,向上的枚举仍然可以进行
{
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
理论上的完全无锁 被破坏在写入这一关。
由于在链表头部增加节点并不是原子操作,所以这里必须增加一个写入锁
这个锁非常非常的小 :
{
_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,我们就可以把它放进这个池中了
}
}
垃圾列表最终定稿
}
}
这里提供一个 MVC的测试聊天室 包括所有源代码
大厅源代码正在googlecode 上用c#缓慢重建 有兴趣的可以下载
wgs-game-lobby.googlecode.com
连接:
游戏大厅 从基础开始(3)——最吸引眼球的部分 客户端与服务器的连接
游戏大厅 从基础开始(3.5)——最吸引眼球的部分 客户端与服务器的连接 的实现
游戏大厅 从基础开始(6)--绕回来细说聊天室(中)之女仆编年史1
游戏大厅 从基础开始(7)--绕回来细说聊天室(中间偏下)之女仆编年史2
游戏大厅 从基础开始(8)--绕回来细说聊天室(下)垃圾列表