在实现了微线程以后再实现消息传递就容易多了,我们也可以把消息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(见上一篇底部的下载链接)中有一个简单的“乒乓”测试。