協程與多線程——消息篇

  在實現了微線程以後再實現消息傳遞就容易多了,我們也可以把消息send和receive看做是一種“阻塞”然後使用統一的調度來實現微線程間的通信。而如果實現了消息傳遞就很容易實現Actor模型了。關於Actor模型推薦大家先看一下老趙的文章(csdn的編輯器不好用了直接貼url:http://www.cnblogs.com/JeffreyZhao/archive/2009/05/11/a-simple-actor-model-implementation.html)。不過我實現消息傳遞的方式和他文章裏給出的實現不一樣,因爲我統一使用微線程“阻塞”的形式並且借鑑了Stackless Python中的Channel機制。所以使用消息傳遞就很簡單建立一個Channel實例,然後在兩個Tasklet實例中一個Send一個Receive就行了,如果沒有發送方或者沒有接收方就會“阻塞”這類似與tcp中的send何receive。而且send和receive本質上就是一種NonBlock,所以消息的分發不需要使用額外的線程來處理。當然如果以後真的需要在進程(或者.net中邏輯進程AppDomain)間通信,你也可以自己實現一個使用外部線程分發消息的Sender和Receiver。好了下面簡要的分析下實現代碼。
public interface IChannel
{
 //是否已連接即send和receive同時存在
 bool IsConnected { get; set; }
 //發送方
 INonBlock Sender { get; set; }
 //接受方
 INonBlock Receiver { get; set; }
 //發送
 INonBlock Send(object msg);
 //接受
 INonBlock Receive();
}
一個很簡單的藉口定義,這裏面需要說明的一點就是消息的格式。因爲C#畢竟不是動態語言所以消息格式還是用object這種方式最簡單,消息發送接收雙方約定消息格式,使用單元測試來做保證。好了再來看看Sender與Receiver的實現:
public class Sender : NonBlock
{
 IChannel _channel;
 public Sender(IChannel channel) : base()
 {
  _channel = channel;
 }

 protected override bool IsSync
 {
  get{ return true; }
 }
 //因爲一定會有Receiver來喚醒這個Sender所在的微線程所以只要同步執行就可以了
 protected override IEnumerable<INonBlock> SyncInvoke()
 {
  yield return Schedule();
  //判斷是否已經連通,因爲下面的操作也可能在Receiver中進行
  if (_channel.IsConnected)
  {
   //更改爲不連通爲了該Channel下次再使用
   _channel.IsConnected = false;
   //喚醒當前微線程任務
   Tasklets.Instance.ResumeTask(this);
   //喚醒接收方微線程任務
   Tasklets.Instance.ResumeTask(_channel.Receiver);
  }
 }

 #region NonBlock
 protected override void AsyncInvoke()
 {
  throw new NotImplementedException();
 }
 #endregion       
}     
Receiver的實現與Sender的實現基本相同,最後我們看看Channel的實現:
public class Channel<T> : IChannel
{
 //一個channel上的send和receiver並不是同步進行的,所以需要緩存發送的消息和接受到得消息
 object _sendMsg;       
 object _receiveMsg;
 //被阻塞的channel
 static readonly Dictionary<IChannel, INonBlock> _channels = new Dictionary<IChannel, INonBlock>();
 //泛型包裝
 public T Msg
 {
  get { return (T)_receiveMsg; }
 }

 private Channel() { }

 public static Channel<T> NewChannel()
 {
  return new Channel<T>();
 }

 public INonBlock Send(T msg)
 {
  return Send((object)msg);
 }
 public INonBlock Send(object msg)
 {
  _sendMsg = msg;
  IChannel iChannel = this as IChannel;
  Sender sender = null;
  //該channel已被Receiver阻塞
  if (_channels.ContainsKey(this))
  {
   var tmp = _channels[this];
   //一個channel只能有一個Sender
   if (tmp is Sender)
    throw new Exception("該Channel已經存在Sender.");
   else
   {
    _channels.Remove(this);                   
    iChannel.IsConnected = true;
    //將消息轉移至接受方消息緩存
    _receiveMsg = _sendMsg;
    _sendMsg = null;                   
   }
  }
  else
  {
   //channel被sender阻塞
   if(sender == null)
    sender = new Sender(this);
   _channels.Add(this, sender);
  }
  iChannel.Sender = sender;
  return sender;
 }
 //Receive方法與Send方法實現基本相同
 public INonBlock Receive()
 {
  //省略...
 }
}
消息傳遞的實現是很簡單的但是它十分強大,在demo(見上一篇底部的下載鏈接)中有一個簡單的“乒乓”測試。

發佈了67 篇原創文章 · 獲贊 8 · 訪問量 22萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章