摘要: 重新开始啃设计模式两年前看设计模式看不懂,因为那时候是打游击,做完了就跑,维护、扩展都谈不上。憋了一年绕着弯路写过了疯狂坦克的服务器大厅后 才真正用身心体会了设计模式的重要。离开了那个游戏公司 也离开了自己写得千疮百孔的烂代码,决定一切重新来过。游戏大厅 从基础开始(1)——最简单的关系,用户与房间 游戏大厅 从基础开始(2)——最基础的交流:聊天 ...  阅读全文
posted @ 2008-12-10 16:03 韦恩卑鄙 v-zhewg @waynebaby 阅读(638) | 评论 (3)编辑

posted @ 2009-10-28 20:36 韦恩卑鄙 v-zhewg @waynebaby 阅读(297) | 评论 (6)编辑
     摘要: 没错,这次章节没有女仆。 前情回顾 我们的最初的需求是建立一个拉模式下用户暂存的顺序信息池 还是这张工作模式图 我们可以把这个需求设计为 Clear:清除所有内容GetEnumerator :实现枚举器,新向旧方向的顺序枚举,这样一旦到达上次读取的时间就可以中断枚举。RecycleFromButtom:从旧向前进行搜索 把满足条件的扔到GCStackOn :把一个新信息放在堆栈的顶部 这就好像是...  阅读全文
posted @ 2009-08-27 14:06 韦恩卑鄙 v-zhewg @waynebaby 阅读(1107) | 评论 (2)编辑

 

老少爷们儿反击战

上一篇中 我们的女仆终于可以做一些像阳光下其他人一样的事情了,少爷们可以和女仆酱一起参加下午茶~ 难得的上流社会啊

这是永远1v1被人私有的女奴 喝茶时被人共有的女仆酱最明显的差异~

明媚的午后阳光下,庭院里白色长餐桌两旁,英俊的少爷们彼此交换着最近的趣闻轶事,一面欣赏女仆酱以1/3几率打翻茶水,可爱而笨笨地努力侍奉着,闪闪发光的样子。

人间极乐喵~~~~~~~~~~   :3

但距离真正的自由~~无Lock还是很远,很远。

因为,

老爷和少爷的调教仍然是一如既往的频繁! 女仆酱你仍然是伟大的老少爷们的东西~!

 

我们看上一次所说的调用聊天室方式:

 

Code Snippet
  1.        Channel cr = session["Chat"];
  2.             cr.Say(Request["text"]);
  3.             foreach (var s in cr.Listen())
  4.             {
  5.                 Response.write("<p>");
  6.                 Response.write(s);
  7.                 Response.write("</p>");
  8.             }

这个行程可不得了

 

  1. 女仆来~~ (get instance)
  2. 调教~!(Say & writelock)
  3. 喝茶~!(Listen & readlock)

所有的少爷和老爷都是这个行程  执事先生,您是恶魔执事来的吧!(塞巴斯酱邪媚一笑)

虽然效率高了近一倍,但是 在奉茶的时候 仍然有调教的队伍大排长龙~

 

image

 

这让老少爷们很是不 不不爽 ! “主人样”们是不会这样满足地口胡!~ 啊 太激动了,咽口水先。

-------------------------------------------------------------------------------------

最年轻的少爷在某个风和日丽的下午茶时间 提出了一个邪恶点子:

“既然我们喝茶的时候只是想看着女仆酱,又不是真的希望她倒茶,为啥我们不能一边看着别人调教女仆酱  一边喝茶呢?”

 

 

 

全体老少爷们的红茶都化作血雾喷洒在无瑕的桌上,纷纷气绝。

 

小少爷解释道:

既然数据池实际上是一个队列  而加入队列仅仅是对队列头有所操作,

那么在已经访问过的节点另一端加入的内容 对于已经开始访问内部内容的线程来说是毫无意义的

 

 image

 

为了“不存在”的变化加锁,这又是何苦呢?

 

“囊得斯嘎!?”

“艘得斯嘎!!”

“游息!!”

爬起来的各位纷纷恍然大悟装,激动地握住彼此的手,泛着泪光。。。。

 

 

 

可是为什么我们之前要加锁呢?

深入研究Queue<T> 的源代码  我们了解到 Queue<T>的实现方法:定长数组实现的循环队列

不熟悉循环队列的兄弟们可以参考博友刺儿头的文章

当每次 ToArray的时候 Queue<T>执行下面的操作

 

Code Snippet
  1.         public T[] ToArray()
  2.         {
  3.             T[] destinationArray = new T[this._size];
  4.             if (this._size != 0)
  5.             {
  6.                 if (this._head < this._tail)
  7.                 {
  8.                     Array.Copy(this._array, this._head, destinationArray, 0, this._size);
  9.                     return destinationArray;
  10.                 }
  11.                 Array.Copy(this._array, this._head, destinationArray, 0, this._array.Length - this._head);
  12.                 Array.Copy(this._array, 0, destinationArray, this._array.Length - this._head, this._tail);
  13.             }
  14.             return destinationArray;
  15.         \

同时有人Enqueue的时候  _size _head  _tail 都可能被并发修改

Code Snippet
  1.     public void Enqueue(T item)
  2.         {
  3.             if (this._size == this._array.Length)
  4.             {
  5.                 int capacity = (int)((this._array.Length * 200L) / 100L);
  6.                 if (capacity < (this._array.Length + 4))
  7.                 {
  8.                     capacity = this._array.Length + 4;
  9.                 }
  10.                 this.SetCapacity(capacity);
  11.             }
  12.             this._array[this._tail] = item;
  13.             this._tail = (this._tail + 1) % this._array.Length;
  14.             this._size++;
  15.             this._version++;
  16.         }

太危险了!

 

我们是为了保障这些关键标记量才用的Lock  这是数组内核本身限制造成的:( 

我们一开始就陷入了  “要用队列,M$提供了队列”的心理陷阱,

——果然观赏女仆酱一定要喝茶是错误的常识

——看来聊天数据池还真的不能拿来主义

 

对于微软类库做不到的 我们一定要有自己的实现。 所以女仆酱啊,你的末日到了欧欧欧欧喝喝喝喝喝喝HiaHiaHia …..

 

敬请期待下一篇:

垃圾列表

posted @ 2009-08-22 22:59 韦恩卑鄙 v-zhewg @waynebaby 阅读(1293) | 评论 (6)编辑

上一篇我们大致的了解了几种聊天室的行为模式

最简单明了的推模式 几乎不需要任何多余的语言来描述它的实现

这一篇我们看看如何实现拉模式更有效。

  

  

  

   

   

本图清晰的表现了""模式聊天室的行为。

  • 并发多用户向数据池写数据
  • 并发多用户从数据池读书据
  • 数据最好以时间为顺序储存在集合中
  • 某时间向后的枚举查找将是最大的消耗。

  

  

聊天室进化 -女仆编年史

神秘的原始社会

仍然参考我们神奇朴素的Asp3聊天室

53     Application.lock
54         Application("show5")=Application("show4") '
一条新信息驾到 第五条信息被淘汰
55         Application("show4")=Application("show3")
56         Application("show3")=Application("show2")
57         Application("show2")=Application("show")
58         Application("show")=NewMessage     '
其他所有的信息向前移动一次给新的信息让个位置。
59     Application.UnLock
60     Response.Write Application("show5")
61     Response.Write Application("show4") '
由于是postback 模式 必须输出历史n行数据
62     Response.Write Application("show3")
63     Response.Write Application("show2")
64     Response.Write Application("show")

从线程安全角度来说 本来 response.write应该也在 application .lock 块中  或者分开两个lock块.  但是这里由于response.write 在非cache模式下可能带来的时间延迟 作者煞费苦心的把他们从安全锁中移动出来.在实际运行中 很可能出现丢话或者重复发言的状况

application究竟 被人做了些什么? 没有边界  没有抽象包装的这个实现就好像原始共产主义 谁是谁的谁啊这都是!

  

 

私有制出现,奴隶社会 LOCK~ 这个女奴是我的~

翻译成c# 我们可以看到一个比较容易理解的逻辑 当然这个代码稍微有所修改 两个锁很明确 很完美的把数据和线程排起了队伍

  

Code Snippet

  1.        class Channel
  2.         {
  3.             Queue<string> MessageQ = new Queue<string>();
  4.  
  5.             public void Say(string message) //写信息
  6.             {
  7.                 lock (MessageQ)
  8.                 {
  9.                     MessageQ.Enqueue(message);
  10.                     while (MessageQ.Count > 5)  // 删多余
  11.                     {
  12.                         MessageQ.Dequeue();
  13.                     }
  14.  
  15.                 }
  16.             }
  17.  
  18.             public string[] Listen() //\u-28781 ?出所有
  19.             {
  20.                 lock (MessageQ)
  21.                 {
  22.                     return MessageQ.ToArray();
  23.  
  24.                 }
  25.  
  26.             }
  27.  
  28.          }

  

在aspx可能这样调用

Code Snippet

  1.             Channel cr = session["Chat"];
  2.             cr.Say(Request["text"]);
  3.  
  4.             foreach (var s in cr.Listen())
  5.             {
  6.                 Response.write("<p>");
  7.                 Response.write(s);
  8.                 Response.write("</p>");
  9.             }

 

看起来圆满完成任务 但是里面充满了暧昧    

 

类似事务 或者访问非托管资源 在访问线程临界资源的时候有个原则

你尽可能的晚锁 尽可能的早释放,

看看刚刚做了些什么

Oh My God

我们可怜的Channel阿  他被全程锁定。好像一个被老爷少爷轮流调教的女奴啊,真让我等正人君子心潮澎湃~~  啊不对  是于心不忍。

由于每个调教者在调教前声明:这个女奴是“我雷瓦Mono”我的东西! 所以在调教者声明 这个女仆“亚没漏”不要了之前 谁也不许碰!

LOCK LOCK~

这才是两个主人并发访问  就已经造成了这么多等待,如果是100主人个并发调教,那得是多么壮观的队伍!

我们的服务程序如果按照这个效率编写 恐怕cpu占用25%的时候就会崩溃---线程队列的极限是多少?  按照Jeffery Richard 的话说 你提出这个问题的时候 就已经Very Very Wrong鸟。

换句话说,不要挑战爷们的耐性,后果很严重

 

 

社会要进步 人民要革命  封建时代来临

我们不能满足这样的性能

老爷调教女奴的时候少爷不观摩 这我们理解,(写的时候加lock防止别的线程读)

少爷和女仆喝茶的时候  老爷不能乱入开始餐厅调教,这我们也接受 (读的时候加lock防止别的线程写)

 

但是少爷们找女奴喝茶,没有道理不可以一起开茶话会吧!

从某种意义上,只要集合元素不变化的话, Queue对象是支持安全的并发读的,为什么几个线程都在读取的状况下,我们还要继续上锁彼此排斥对方呢? 我们只是纯粹对女仆有爱,没什么不可以光明正大的吧!

把锁从完全锁变成读写锁,能够有效的减少很多不需要的等待。——  我们可以把喝茶的队伍缩短!

 

Code Snippet
  1.        class ChannelReadWriteLock
  2.         {
  3.             Queue<string> MessageQ = new Queue<string>();
  4.             System.Threading.ReaderWriterLockSlim _lock = new System.Threading.ReaderWriterLockSlim();
  5.             public void Say(string message) //写信息
  6.             {
  7.                 _lock.EnterWriteLock();
  8.                 
  9.                     MessageQ.Enqueue(message);
  10.                     while (MessageQ.Count > 5)  // 删多余
  11.                     {
  12.                         MessageQ.Dequeue();
  13.                     }
  14.                 _lock.ExitWriteLock ();
  15.             }
  16.             public string[] Listen() //   \u-29701 ?所有
  17.             {
  18.                  _lock.EnterReadLock ();
  19.                   var ary= MessageQ.ToArray();
  20.                 _lock.ExitReadLock();
  21.                 return ary;
  22.             }
  23.         \

这是女仆界的胜利 她不再是一个人(的人) 而是可以和人socal的普通人了  虽然还在封建家长制的阴影下,仍然是被剥削被蹂躏的底层民众,但是她已经具有了比以前更大的自由!

 

先写到这里看看和谐底限 敬请期待   女仆编年史2

posted @ 2009-08-21 23:40 韦恩卑鄙 v-zhewg @waynebaby 阅读(1650) | 评论 (5)编辑
     摘要: 距离上次写大厅已经有几个月。中间工作繁忙,待学习的东西又很多,又有很多新产品的想法想实践,一直把这个坑闲置着。直到前两天简单的树遍历枚举器-挑战一个程序员到底能多懒 装配脑袋的几个回帖给我带来一些灵感,让我又重新看看我聊天频道的实现,发现竟然有很多可以推敲重新处理的地方。昨天上班的时候又把聊天部分整理了下,愿和大家分享。Wiki定义的聊天室网络聊天室通常直称聊天室,是一种人们可以在线交谈的的网络论...  阅读全文
posted @ 2009-08-19 20:41 韦恩卑鄙 v-zhewg @waynebaby 阅读(2365) | 评论 (11)编辑
     摘要: 前一阵在递归算法相关回贴的讨论中 和某lz抱怨 现在的同志们连用自己的栈加循环模拟递归都不会做了。如果自己实现递归栈 又怎么会在线程栈中储存过多无关信息?数据全部都在堆里 又怎会stackoverflow?当时就有想法自己实现一个,造福一下群众,但是被坏心眼的某lz 阻止了。“让他们自己写。” 他大概是这么说滴,“他们自己写了才算懂得了。”可是 最近...  阅读全文
posted @ 2009-08-16 01:53 韦恩卑鄙 v-zhewg @waynebaby 阅读(2330) | 评论 (41)编辑
     摘要: 最近玩silverlight3 需要把一个 plateprojection clone作为sealed class 不大好clone 于是想了个别的办法[代码]用的时候很方便  阅读全文
posted @ 2009-06-17 00:20 韦恩卑鄙 v-zhewg @waynebaby 阅读(236) | 评论 (0)编辑
     摘要: 公司需要我做 Silver Light rearch 发现silver light 数据绑定多少有点痛苦 object datasource 要等到 vs2010 于是自己写了一个 用得到的自然会看懂 哼哼 [代码]页面代码[代码]Web Service[代码]  阅读全文
posted @ 2009-02-27 10:50 韦恩卑鄙 v-zhewg @waynebaby 阅读(663) | 评论 (4)编辑