感谢@Benoit Vey 提供的帮助。
确实如此,在查询的执行与生产者向消费者执行其 tick() 消息传递的时间之间没有明示或暗示的偶然性。
从这个意义上说,没有巫毒,没有魔法。这一切都表现得像任何参与者系统所期望的那样。
Actor 中的消息按顺序处理(应该如此)。因此,为了获得所需的程序行为,生产者应该最终触发查询(按照处理produceOne
消息后的顺序)。
在这里,如何实现:
use "collections"
actor Consumer
var _received : I32
new create() =>
_received = 0
be tick() =>
_received = _received + 1
be query(main : Main) =>
main.status(_received)
actor Producer
var _consumer : Consumer
new create(consumer' : Consumer) =>
_consumer = consumer'
be produceOne () =>
_consumer.tick()
be forward (main : Main) =>
main.doQuery(_consumer)
actor Main
var _env : Env
new create(env: Env) =>
_env = env
let c : Consumer = Consumer.create()
let p = Producer.create(c)
for i in Range[I32](0,10) do
p.produceOne()
end
//c.query(this)
p.forward(this)
be doQuery (target : Consumer) =>
target.query(this)
be status( count : I32) =>
// let fortyTwo : I32 = 42
// _env.out.print( "fortytwo? " + (fortyTwo.string()))
_env.out.print( "produced: " + (count.string()) )
只是为了咯咯笑(和比较),我也在 F# 中实现了相同的功能。令我惊讶的是,小马在紧凑性类别中获胜。Pony 中 39 行代码,F# 中 80 行。再加上原生代码生成,Pony 确实是一个有趣的语言选择。
open FSharp.Control
type ConsumerMessage =
| Tick
| Query of MailboxProcessor<MainMessage>
and ProducerMessage =
| ProduceOne of MailboxProcessor<ConsumerMessage>
| Forward of (MailboxProcessor<MainMessage> * MainMessage)
and MainMessage =
| Status of int
| DoQuery of MailboxProcessor<ConsumerMessage>
let consumer =
new MailboxProcessor<ConsumerMessage>
(fun inbox ->
let rec loop count =
async {
let! m = inbox.Receive()
match m with
| Tick ->
return! loop (count+1)
| Query(target) ->
do target.Post(Status count)
return! loop count
}
loop 0
)
let producer =
new MailboxProcessor<ProducerMessage>
(fun inbox ->
let rec loop () =
async {
let! m = inbox.Receive()
match m with
| ProduceOne(consumer') ->
consumer'.Post(Tick)
return! loop ()
| Forward (target, msg) ->
target.Post(msg)
return! loop ()
}
loop ()
)
let main =
new MailboxProcessor<MainMessage>
(fun inbox ->
let rec loop () =
async {
let! m = inbox.Receive()
match m with
| Status(count) ->
printfn "Status: %d" count
return! loop ()
| DoQuery(target) ->
target.Post(Query inbox)
return! loop ()
}
loop ()
)
let init() =
main.Start()
consumer.Start()
producer.Start()
let run() =
for _ in [1..10] do
producer.Post(ProduceOne consumer)
producer.Post(Forward(main,DoQuery consumer))
let query () =
consumer.Post(Query main)
let go() =
init ()
run ()
//query ()