Edited at

複数のゴルーチンで相互に通信するやつ

More than 3 years have passed since last update.

https://github.com/peco/peco

を参考に複数のゴルーチンを動かしながら相互に通信するためのハブチャンネル?を作ってみました

以下のコードは非常にgolangらしからぬものです。

自戒のために残しておきますが、読者に於かれましては参考にされないようお願い致します。


ライブラリ


hub/hub_lib.go

package hub

// interface HubReq
type HubReq interface {
Reply(HubReq)
Data() interface{}
}

// Base Struct for implementation of HubReq
type HubReqBase struct {
replyCh HubChannel
data interface{}
}

func (h *HubReqBase) Reply(r HubReq){
h.replyCh.Send(r)
}

func (h *HubReqBase) Data() interface{} {
return h.data
}

// ctor of HubReqBase
func New(ch HubChannel, data interface{}) *HubReqBase{
return &HubReqBase{ ch, data }
}

// interface HubChannel
type HubChannel interface {
Send(HubReq)
Loop()
GetCh() chan HubReq
}

// Base Struct for implementation of HubChannel
type HubChannelBase struct {
ch chan HubReq
}

func (h *HubChannelBase) GetCh() chan HubReq {
return h.ch
}

func (h *HubChannelBase) Send(req HubReq){
h.ch <- req
}

// ctor of HubChannelBase
func NewHubChannelBase() *HubChannelBase{
return &HubChannelBase {
make(chan HubReq, 5),
}
}

// Base Struct for Hub
type HubBase struct {
quit chan bool
}

func (h *HubBase) Wait(){
<-h.quit
}

func (h *HubBase) Quit(){
h.quit <- true
}

// ctor of HubBase
func NewHubBase() *HubBase{
return &HubBase{
make(chan bool),
}
}



実装例

簡単なエコープログラムで実装してみました

sqrt 2.5とかsquare 5.1とか打つと計算してくれます

q, quit, exitのどれか入れると終了します


hub/hub_main.go

package hub

type Hub struct {
*HubBase

Model HubChannel
View HubChannel
Controller HubChannel
}

func (h *Hub) Loop() {
go h.Model.Loop()
go h.View.Loop()
go h.Controller.Loop()

h.Wait()
}

func NewHub() *Hub {
h := &Hub{ NewHubBase(), nil, nil, nil }
h.Model = NewModel(h)
h.View = NewView(h)
h.Controller = NewController(h)
return h
}



hub/model.go

package hub

import "fmt"
import "math"

type CalcSqrt struct { *HubReqBase }
type CalcSquare struct { *HubReqBase }

type Model struct {
*Hub
*HubChannelBase
}

func (m *Model) Loop(){
for {
req := <- m.GetCh()

switch req := req.(type) {
case CalcSqrt:
x := req.Data().(float64)
data := fmt.Sprintf("Sqrt(%f) = %f", x, math.Sqrt(x))

m.View.Send(UpdateMsg{ New(m, data) })
case CalcSquare:
x := req.Data().(float64)
data := fmt.Sprintf("%f^2 = %f", x, x * x)

m.View.Send(UpdateMsg{ New(m, data) })
default:
_ = req
}

}
}

func NewModel(h *Hub) *Model {
return &Model{ h, NewHubChannelBase() }
}



hub/view.go

package hub

import "fmt"

type UpdateMsg struct { *HubReqBase }

type View struct {
*Hub
*HubChannelBase
}

func (v *View) Loop(){
for {
req := <- v.GetCh()

switch req := req.(type) {
case UpdateMsg:
msg := req.Data().(string)
fmt.Printf("> %s\n", msg)
default:
_ = req
}
}
}

func NewView(h *Hub) *View {
return &View{ h, NewHubChannelBase() }
}



hub/controller.go

package hub

import (
"bufio"
"os"
"fmt"
)

type Controller struct {
*Hub
*HubChannelBase
}

func (c *Controller) Loop(){
var err error
scanner := bufio.NewScanner(os.Stdin)

for scanner.Scan(){
line := scanner.Text()
if line == "" {
continue
}

if line == "q" || line == "quit" || line == "exit" {
c.Quit()
break
}

hasSent := false
var num float64

_, err = fmt.Sscanf(line, "sqrt %f", &num)
if !hasSent && err == nil {
c.Model.Send(CalcSqrt{ New(c, num) })
hasSent = true
}

_, err = fmt.Sscanf(line, "square %f", &num)
if !hasSent && err == nil {
c.Model.Send(CalcSquare{ New(c, num) })
hasSent = true
}

if !hasSent {
c.View.Send(UpdateMsg{ New(c, line) })
}
}
}

func NewController(h *Hub) *Controller {
return &Controller{ h, NewHubChannelBase() }
}



呼び出し


main.go

package main

import (
"./hub"
)

func main(){
h := hub.NewHub()
h.Loop()
}