简单例子:
# 摘自:http://www.stackless.com/wiki/Channels import stackless def Sending(channel): print "sending" channel.send("foo") def Receiving(channel): print "receiving" print channel.receive() ch=stackless.channel() task=stackless.tasklet(Sending)(ch) task2=stackless.tasklet(Receiving)(ch) stackless.run()
-module(message). -export([start/0]). sending(Pid) -> io:format("sending~n"), Pid ! foo. receiving(Pid) -> io:format("receiving~n"), receive X -> io:format("~p~n", [X]) end, Pid ! done. start() -> Pid = spawn(fun() -> receiving(self()) end), sending(Pid), receive X -> X end.
Erlang 的消息是通过进程邮箱(mailbox)来传递的,每进程绑定一个邮箱。
Stackless Python 消息通过 Channel 传递,Channel 是可以任意创建的,这有一个优点是你可以在同一个进程中使用数据 Channel 和控制 Channel,甚至根据功能来分割。
还有个更好的场景说明它的好处,在一个 server S 进程中向另一个 server X 发送请求,server S 的 mailbox 可能同时存有来自 client 和 server X 的消息,其它语言不一定能很方便地实现 Selective Receive,接收会比较麻烦,即便是 Erlang,这也是有成本的。
如果在向 server X 请求时创建一个新的 Channel 用来接收消息,就完全没有这个麻烦了,Erlang 中要实现这个功能应该也不是麻烦事,又造了一个轮子。
Erlang也可以在每个消息到来时,创建一个进程来处理,也可以避免这个问题,和创建 Channel 效果是一样的。
在使用 ucontext 实现的轻量级线程环境中,还是尽可能减少线程创建的开销,创建一个 Channel 很容易,实现多个 Channel 的 Poll 就更强大了。Channel 还可以使用泛型实现,约束传递的类型。
-----------------------------------------------------
补上我的Channel C++实现介绍,待整理。
templateclass ChannelT : public Channel { public: // ... void Send(const T& v); T Receive(int timeout=-1); };
使用:
typedef ChannelTIntChannel; typedef shared_ptr IntChannelPtr; void process1(IntChannelPtr channel) { for(int i=0; i<10; i++) { channel->Send(1); Sleep(1000); } channel->Send(-1); // 退出消息 } void process2(IntChannelPtr channel) { while (true) { int i = channel->Receive(); // ... } } IntChannelPtr channel = new IntChannel; SPAWN(&process1, channel); SPAWN(&process2, channel);
这和 mailbox 相似,不过process1可以省去一个 mailbox。
使用Channel名字:
Cid cid; // Channel名字注册 void foo_process() { ChannelTchannel; cid = channel.cid; // 给其它进程使用 RegisterChannel("foo", channel); // 注册Channel名字,也可以使用ID int i = channel.Receive(); // ... } ChannelT channel("foo"); // 通过名字发送给Node内Channel channel.Send(1); ChannelT channel(cid); // 通过Channel ID发送 channel.Send(1);
Channel名字代替了进程名字,ChannelID代替了ProcessID,以前每创建一个进程就要生成一个PID,现在只有使用Channel才生成CID,Channel的创建和销毁开销比进程小。多个进程在同一个Channel上等待消息时,进程要挂在Channel的等待链上。
直接使用远程Channel:
ChannelTchannel("[email protected]"); channel.Send(1);
Channel也可以传递到其它Node上,对方得到此Channel时直接调用它的Send就可以发送消息回来。
在通讯层面上,Channel是逻辑概念,它通过Node之间的连接来传递数据,和Erlang相似。