【发布时间】:2021-12-26 01:11:23
【问题描述】:
我正在尝试为一种元胞自动机编写观察者模式或发布/提交模式。
经典的观察者模式并不适用,因为如果单元 A 订阅单元 B 中的更改,反之亦然,由于递归方法,应用程序将耗尽堆栈(B.update() 将调用 A .update() 等,应用程序将耗尽堆栈)。
所以我考虑使用发布/订阅模式,其中各个单元格相互传递消息,而不是调用彼此的 update() 方法。
这是一个简单的例子,有两个单元格 A 和 B:
package main
import (
"fmt"
ps "publish/pubsub"
)
func main() {
fmt.Printf("Starting\n")
chEnd := make(chan int)
// initialize
a := ps.NewNode(1, 0)
b := ps.NewNode(2, 0)
// connect nodes
a.Connect(b.ChOut)
b.Connect(a.ChOut)
// Start listening
a.Listen()
b.Listen()
// Start sending data on one arbitrary node
// to start the process.
a.ChIn <- 10
<-chEnd
}
以及对应的库
package pubsub
import (
"fmt"
)
type Node struct {
Id int
State int
ChOut chan int
ChIn chan int
}
func NewNode(id int, state int) Node {
chout := make(chan int)
var chin chan int
return Node{id, state, chout, chin}
}
func (p *Node) Broadcast(inItem int) {
p.ChOut <- inItem + 1
//time.Sleep(100 * time.Millisecond)
}
func (p *Node) Listen() {
go func() {
for {
select {
case inItem := <-p.ChIn:
fmt.Printf("%d: %d\n", p.Id, inItem)
p.Broadcast(inItem)
}
}
}()
}
func (p *Node) Connect(ch chan int) {
p.ChIn = ch
}
每个节点都有一个输入和一个输出通道。 B的输入通道是A的输出通道,反之亦然。
每次更新都只是增加另一个单元格传递的数据。
它似乎工作。到目前为止,一切顺利。
我尝试用一组 4 个单元 A、B、C、D 做类似的事情,以模拟各种类型的一维元胞自动机。
在第二次尝试中, 每个单元都有两个输入通道(let 和 right)来分别监听其最近的左手和右手邻居(ChinL 和 ChinR)。 每个单元必须输出通道以将其最新更新的状态传达给其最近的邻居(ChoutL 和 ChoutR)。 在使用 4 个单元格执行该方案时,我一定做错了什么,因为它产生了奇怪的结果:在 4 个单元格之间来回传递的值似乎达到了阈值,而不是在每个连续步骤中都增加:这是代码:
package main
import (
"fmt"
ps "publish/pubsub"
)
func main() {
fmt.Printf("Starting\n")
chEnd := make(chan int)
// initialize
a := ps.NewNode(1, 0)
b := ps.NewNode(2, 0)
c := ps.NewNode(3, 0)
d := ps.NewNode(4, 0)
// connect nodes
a.ChInL = d.ChOutR
a.ChInR = b.ChOutL
b.ChInL = a.ChOutR
b.ChInR = c.ChOutL
c.ChInL = b.ChOutR
c.ChInR = d.ChOutL
d.ChInL = c.ChOutR
d.ChInR = a.ChOutL
// Start listening
go a.Listen()
go b.Listen()
go c.Listen()
go d.Listen()
go a.Broadcast()
go b.Broadcast()
go c.Broadcast()
go d.Broadcast()
// Start sending data on one arbitrary node
// to start the process.
a.ChInL <- 1
// Dummy read on channel to make main() wait
<-chEnd
}
/*
A B C D
LR LR LR LR
*/
以及对应的库
package pubsub
import (
"fmt"
"strings"
)
type Node struct {
Id int
State int
ChOutL chan int
ChOutR chan int
ChInL chan int
ChInR chan int
ChIO chan int
}
func NewNode(id int, state int) Node {
choutL := make(chan int)
choutR := make(chan int)
var chinL chan int
var chinR chan int
chIO := make(chan int)
return Node{id, state, choutL, choutR, chinL, chinR, chIO}
}
func (p *Node) Broadcast() {
for item := range p.ChIO {
p.ChOutL <- item + 1
p.ChOutR <- item + 1
fmt.Printf("%d: %d %s\n", p.Id, item, strings.Repeat("*", item))
}
}
func (p *Node) Listen() {
for {
//time.Sleep(100 * time.Millisecond)
select {
case inItem := <-p.ChInL:
go func() {
p.ChIO <- inItem
}()
case inItem := <-p.ChInR:
go func() {
p.ChIO <- inItem
}()
}
}
}
为了完整起见,这里是上述模块的 go.mod:
module publish
go 1.17
【问题讨论】:
标签: go publish-subscribe observer-pattern channels