天下に卓球をしないところはない
18306 ワード
メッセージング(Message Passing)の分野では、PingPongは最も一般的なテストの一つです.その機能は簡単で少し退屈で、1つのPing Actorと1つの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が欠けていることがわかります.:)
言語の特性が異なるため,あるActorモデルは言語に組み込まれており(例えばErlang),その他の多くはフレームワークによって補完されている.一部のActorモデルでは文字列をメッセージとして使用しているが,言語機能によって強いタイプのメッセージを伝えることができる.様々な実装を簡単に羅列し,これらの実装を表面的に簡単に対比することも興味深いといえる.もちろん,これらの実装はPingPongをプレゼンテーションとして用いたが,その詳細はわずかに異なる.例えば、Ping/Pongプロセスを永遠に持続させるものもあれば、しばらくしてから中止させるものもあります.コマンドを認識する人もいれば、気にしない人もいます.趙さんはこれらの違いは上品ではないと思っているが,これについても何の修正もしていない.
この文書では、以下のActorモデルの実装について説明します(多くの言語では構文シェーディングが欠けていますが、使いやすいHTMLハイライトスキームがある場合は、説明を惜しまない).
これらの例では、趙さんが次回単独で説明して紹介するつもりなので、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
let ping iters (outbox : MailboxProcessor) =
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