技术开发 频道

天下无处不乒乓

 【IT168技术文档】
转载自Jeff Zhao的博客

   
在消息传递(Message Passing)领域,PingPong是最常见的测试之一。它的功能简单的有些无聊,一个Ping Actor和一个Pong Actor之间互相传递消息,你Ping过来我Pong过去。也正因为如此简单,PingPong的目标仅仅是测试纯粹的消息传递机制的效率。也正因为如此,各Actor模型往往都将其作为展示自己功能的第一个示例。老赵从互联网上收集了一些最为常见的,不同语言/平台下Actor模型实现PingPong的示例,可作“观赏”之用。

 由于语言特性不同,有的Actor模型内置于语言之中的(如Erlang),其他大都由框架进行补充。有的Actor模型使用字符串作为消息,有的却通过语言功能而可以传递强类型的消息。简单的罗列各种实现,把这些实现从表面上进行简单的对比,也可以说颇有趣味。当然,这些实现虽然都使用了PingPong作为演示,但是其中细节还是略有不同的。例如有些会让Ping/Pong过程永远持续下去,而有些会让它们在进行一段时间过后就中止;有些会对命令进行识别,而有些对此并不在意。老赵认为这些区别无伤大雅,对此也未作任何修改。

 本文会涉及以下Actor模型的实现(不少语言缺少语法着色,如果您有好用的HTML高亮方案请不吝指明):
Erlang (source)

 Scala (source)

 Haskell (source)

 Ruby (source)

 Python (source)

 Axum with Channel (source)

 Axum with Ordered Interaction Points (source)

 F# (source)

 F# with ActorLite (source)

 在这些示例中,您会发现缺少了C#下的Actor——因为老赵打算下次单独开篇对此进行说明和介绍。:)

 Erlang

 -module(tut15).

 -export([start/0, ping/2, pong/0]).

 ping(0, Pong_PID) ->

 Pong_PID ! finished,

 io:format("ping finished~n", []);

 ping(N, Pong_PID) ->

 Pong_PID ! {ping, self()},

 receive

 pong ->

 io:format("Ping received pong~n", [])

 end,

 ping(N - 1, Pong_PID).

 pong() ->

 receive

 finished ->

 io:format("Pong finished~n", []);

 {ping, Ping_PID} ->

 io:format("Pong received ping~n", []),

 Ping_PID ! pong,

 pong()

 end.

 start() ->

 Pong_PID = spawn(tut15, pong, []),

 spawn(tut15, ping, [3, Pong_PID]).

 Scala

import scala.actors.Actor

 import scala.actors.Actor._

 class Ping(count: int, pong: Actor) extends Actor {

 def act() {

 var pingsLeft = count - 1

 pong ! Ping

 loop {

 react {

 case Pong =>

 if (pingsLeft % 1000 == 0)

 Console.println("Ping: pong")

 if (pingsLeft > 0) {

 pong ! Ping

 pingsLeft -= 1

 } else {

 Console.println("Ping: stop")

 pong ! Stop

 exit()

 }

 }

 }

 }

 }

 class Pong extends Actor {

 def act() {

 var pongCount = 0

 loop {

 react {

 case Ping =>

 if (pongCount % 1000 == 0)

 Console.println("Pong: ping "+pongCount)

 sender ! Pong

 pongCount = pongCount + 1

 case Stop =>

 Console.println("Pong: stop")

 exit()

 }

 }

 }

 }

 object pingpong extends Application {

 val pong = new Pong

 val ping = new Ping(100000, pong)

 ping.start

 pong.start

 }

 Ruby

 require 'concurrent/actors'

 include Concurrent::Actors

 Message = Struct.new :ping, :pong

 ping_thread = Actor.spawn do

 loop do

 Actor.receive do |f|

 f.when Message do |m|

 puts "PING"

 sleep(1)

 m.pong << Message.new(m.ping, m.pong)

 end

 end

 end

 end

 pong_thread = Actor.spawn do

 loop do

 Actor.receive do |f|

 f.when Message do |m|

 puts "PONG"

 sleep(1)

 m.ping << Message.new(m.ping, m.pong)

 end

 end

 end

 end

 ping_thread << Message.new(ping_thread, pong_thread)

 while(true)

 puts("WAITING...")

 sleep(5)

 end

 Python

#

 # pingpong_stackless.py

 #

 import stackless

 ping_channel = stackless.channel()

 pong_channel = stackless.channel()

 def ping():

 while ping_channel.receive(): #blocks here

 print "PING"

 pong_channel.send("from ping")

 def pong():

 while pong_channel.receive():

 print "PONG"

 ping_channel.send("from pong")

 stackless.tasklet(ping)()

 stackless.tasklet(pong)()

 # we need to 'prime' the game by sending a start message

 # if not, both tasklets will block

 stackless.tasklet(ping_channel.send)('startup')

 stackless.run()

0
相关文章