技术开发 频道

天下无处不乒乓

 【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()

 Axum with Channel

  using System;

 using System.Concurrency;

 using System.Collections.Generic;

 using Microsoft.Axum;

 using System.Concurrency.Messaging;

 public channel PingPongStatus

 {

 input int HowMany;

 output int Done;

 Start: { HowMany, Done -> End; }

 }

 public agent Ping : channel PingPongStatus

 {

 public Ping()

 {

 // How many are we supposed to do?

 var iters = receive(PrimaryChannel::HowMany);

 // Create pong and send how many to do

 var chan = Pong.CreateInNewDomain();

 chan::HowMany <-- iters;

 // Send pings and receive pongs

 for (int i = 0; i < iters; i++)

 {

 chan::Ping <-- Signal.Value;

 receive(chan::Pong);

 Console.WriteLine("Ping received Pong");

 }

 // How many did Pong process?

 int pongIters = receive(chan::Done);

 PrimaryChannel::Done <-- 0;

 }

 }

 public channel PingPong

 {

 input int HowMany;

 output int Done;

 input Signal Ping;

 output Signal Pong;

 }

 public agent Pong : channel PingPong

 {

 public Pong()

 {

 // Get how many we're supposed to do

 var iters = receive(PrimaryChannel::HowMany);

 int i = 0;

 // Receive our ping and send back our pong

 for (; i < iters; i++)

 {

 receive(PrimaryChannel::Ping);

 Console.WriteLine("Pong received Ping");

 PrimaryChannel::Pong <-- Signal.Value;

 }

 PrimaryChannel::Done <-- i;

 }

 }

 public agent Program : channel Application

 {

 public Program()

 {

 // Wait for our command args

 var cmdArgs = receive(PrimaryChannel::CommandLine);

 // Set the iterations to be some number

 var iters = 3;

 // Create instance of ping and send msg

 var chan = Ping.CreateInNewDomain();

 chan::HowMany <-- iters;

 // Receive how many done

 receive(chan::Done);

 PrimaryChannel::Done <-- Signal.Value;

 }

 }

 F#

 open System

 type message = Finished | Msg of int * MailboxProcessor<message>

 let ping iters (outbox : MailboxProcessor<message>) =

 MailboxProcessor.Start(fun inbox ->

 let rec loop n = async {

 if n > 0 then

 outbox.Post( Msg(n, inbox) )

 let! msg = inbox.Receive()

 Console.WriteLine("ping received pong")

 return! loop(n-1)

 else

 outbox.Post(Finished)

 Console.WriteLine("ping finished")

 return ()}

 loop iters)

 let pong () =

 MailboxProcessor.Start(fun inbox ->

 let rec loop () = async {

 let! msg = inbox.Receive()

 match msg with

 | Finished ->

 Console.WriteLine("pong finished")

 return ()

 | Msg(n, outbox) ->

 Console.WriteLine("pong received ping")

 outbox.Post(Msg(n, inbox)

 return! loop() }

 loop())

 let ponger = pong()

 do (ping 10 ponger) |> ignore

 F# with ActorLite

 #light

 open System

 open ActorLite

 let (<=) (m:_ Actor) msg = m.Post msg

 type message = string * message Actor

 let pong =

 { new message Actor() with

 override self.Receive(message) =

 let msg, ping = message

 Console.WriteLine(msg)

 ping <= ("Pong", self) }

 let ping =

 { new message Actor() with

 override self.Receive(message) =

 let msg, pong = message

 Console.WriteLine(msg)

 pong <= ("Ping", self) }

 ping <= ("Start", pong)

 Console.ReadLine |> ignore

原文地址:http://www.cnblogs.com/jeffreyzhao/archive/2009/06/24/everything-ping-pong.html

0
相关文章