在實現了微線程以後再實現消息傳遞就容易多了,我們也可以把消息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(見上一篇底部的下載鏈接)中有一個簡單的“乒乓”測試。