当前位置:首页 > 开发 > 编程语言 > 编程 > 正文

用go实现erlang模型

发表于: 2012-01-10   作者:bookjovi   来源:转载   浏览次数:
摘要:   以前说过Go的goroutine+channel模式比erlang的process+message模式更灵活和通用,可以说erlang的模式是go模式的子集subset,那么用golang可以实现erlang那样的process模式吗?那样go就看起来像erlang了   erlang的process有着built-in的可靠性,通过link和monitor使得每个p

 

以前说过Go的goroutine+channel模式比erlang的process+message模式更灵活和通用,可以说erlang的模式是go模式的子集subset,那么用golang可以实现erlang那样的process模式吗?那样go就看起来像erlang了

 

erlang的process有着built-in的可靠性,通过link和monitor使得每个process的运行可靠性加强,golang中的goroutine可没有可靠性一说,不过go中还是可以通过defer实现erlang的可靠性的。

花了几个小时的时间写了一个go package,暂命名为erlang.go,功能是在go中实现erlang process模型,下面看代码:

package erlang

type Pid chan interface {}

type link struct {
        next *link
        pid Pid
}

type Monitor struct {
        next *Monitor
        pid Pid
}

type Process struct {
        name      string
        fun       func(Pid)
        status    int
        linklist *link
        exit_reason interface {}
}

const MsgCap = 1000

var process_reg = make(map[string]Pid)
var link_map = make(map[Pid]*link)
var monitor_map = make(map[Pid]*link)
var processes_map = make(map[Pid]*Process)


func Get_process_name(process *Process) (string){
        return process.name
}

func Get_process_exitreason(process *Process) (interface {}){
        return process.exit_reason
}

func SpawnProcess(process *Process) (Pid) {
        return Spawn(process.fun)
}

func IsAlive(pid Pid) (bool) {
        return (processes_map[pid] != nil)
}

func CountProcesses() int {
        i := 0
        for _, v := range processes_map {
                if v != nil {
                        i++
                }
        }
        return i
}

func Register(name string, pid Pid) {
        if process_reg[name] != nil {
                return
        }
        process_reg[name] = pid
        processes_map[pid].name = name
}

func Clear_name(name string) {
        process_reg[name] = nil
}

func Getpid(name string) (Pid) {
        return process_reg[name]
}

func Spawn(fun func(Pid)) (Pid) {
        pid := make(Pid, MsgCap)
        process := new(Process)
        process.fun = fun
        processes_map[pid] = process

        go func() {
                defer func() {
                        if x := recover(); x != nil {
                                process.exit_reason = x
                        }
                        for this := process.linklist; this != nil; this = this.next {
                                this.pid<- process
                        }
                        processes_map[pid] = nil
                        close(pid)
                }()
                fun(pid)
        }()
        return pid
}

func Addlink(supervisor, pid Pid) {
        newlink := new(link)
        newlink.pid = supervisor

        tmp := processes_map[pid].linklist
        processes_map[pid].linklist = newlink
        newlink.next = tmp
}

func Spawn_link(supervisor Pid, fun func(Pid)) (Pid){
        return Spawn(func(self Pid) {
                Addlink(supervisor, self)
                fun(self)
        })
}

func Spawn_name(name string, fun func(Pid)) (Pid) {
        return Spawn(func(self Pid) {
                Register(name, self)
                defer Clear_name(name)
                fun(self)
        })
}

func Spawn_link_name(name string, supervisor Pid, fun func(Pid)) (Pid) {
        return Spawn_link(supervisor, func(self Pid) {
                Register(name, self)
                defer Clear_name(name)
                fun(self)
        })
}

 

再看看测试代码:

package main

import ("runtime"
        "time"
        "fmt"
        "erlang"
)

////////////////////////////////////// testing code //////////////////////////////////////////
func server(self erlang.Pid) {
        for {
        select {
        case msg := <-self:
                fmt.Println("server receive msg: ", msg)
                if msg == "panic" {
                        panic("some one kill me")
                }
        }
        }
}

func client(self, peer erlang.Pid) {
        peer<- 1
        peer<- "one"
        peer<- "two"

        time.Sleep(time.Second)   //wait name service registe success
        erlang.Getpid("server")<- 2

        erlang.Getpid("server")<- "panic"  // kill server

        time.Sleep(time.Second)  // wait server restart
        erlang.Getpid("server")<- "three"

        peer<- "three"  // send on closed channel
}

func supervisor(self erlang.Pid) {
        for {
                msg := <-self

                process, _ := msg.(*erlang.Process)
                fmt.Println("supervisor: process terminated:", erlang.Get_process_name(process),
                                        "\treason:", erlang.Get_process_exitreason(process))

                switch erlang.Get_process_name(process) {
                case "server":
                        erlang.SpawnProcess(process)
                }
        }
}

func main() {
        runtime.GOMAXPROCS(3)

        supervisor := erlang.Spawn(func(self erlang.Pid) {
                supervisor(self)
        })

        pid1 := erlang.Spawn_name("server", func(self erlang.Pid) {
                server(self)
        }); erlang.Addlink(supervisor, pid1)

        pid2 := erlang.Spawn_name("client", func(self erlang.Pid) {
                client(self, pid1)
        }); erlang.Addlink(supervisor, pid2)

        fmt.Println("server alived:", erlang.IsAlive(pid1))
        fmt.Println("client alived:", erlang.IsAlive(pid2))
        fmt.Println("CountProcesses:", erlang.CountProcesses())

        time.Sleep(10*time.Second)
        fmt.Println("server alived:", erlang.IsAlive(pid1))  // should be false
        fmt.Println("server Getpid alived:", erlang.IsAlive(erlang.Getpid("server")))  // should be true
        fmt.Println("client alived:", erlang.IsAlive(pid2))  // should be true
        fmt.Println("CountProcesses:", erlang.CountProcesses())
}

 

示例很清楚,实现功能如下:

1) spawn

像erlang那样调用erlang.Spwan生成一个process,返回一个pid,

2)message

可以向任何pid发送任何message,可以发数字,也可以发字符串。

3)supervision

erlang.go中也实现了supervision功能,调用erlang.Addlink指定supervisor和需监控的pid,当process退出时(无论异常退出还是正常退出),supervisor将会收到消息。这里每个process就像erlang中那样相互隔离。可以指定某些process的行为,如server类的process退出后可以re-spawn

4)name service

可以给每个process注册name,就像erlang的register,可以向指定name的process发送消息

 

有了erlang.go的帮助后,erlang丰富的otp library很多可以架构在go之上了,这样就可以弥补了go在library上的欠缺

 

下面是示例程序的运行结果:

[root@localhost test]# go run test.go  
server alived: true
server receive msg:  1
server receive msg:  one
server receive msg:  two
client alived: true
CountProcesses: 3
server receive msg:  2
server receive msg:  panic
supervisor: process terminated: server  reason: some one kill me
server receive msg:  three
supervisor: process terminated: client  reason: runtime error: send on closed channel

server alived: false
server Getpid alived: true
client alived: false
CountProcesses: 2
 

本人一直比较喜欢erlang的genfsm,所以想在go中实现像genfsm那样的gofsm,可惜go中没有atom,go中没有apply,还是很难实现genfsm那样的简洁。

 

用go实现erlang模型

  • 0

    开心

    开心

  • 0

    板砖

    板砖

  • 0

    感动

    感动

  • 0

    有用

    有用

  • 0

    疑问

    疑问

  • 0

    难过

    难过

  • 0

    无聊

    无聊

  • 0

    震惊

    震惊

编辑推荐
Erlang的原子(atom)在匹配中有着重要作用,它兼顾了可读性和运行效率。 通过atom,可以实现很多灵活
用gmf实现业务模型编辑器 过去用Graphical Editor Framework(GEF)实现业务模型编辑器既慢又痛苦,光
websocket分为握手和数据传输阶段,即进行了HTTP握手 + 双工的TCP连接 RFC协议文档在:http://tools
之前研究了一个问题"[Erlang 0050]用fun在Erlang Shell中编写尾递归",一直对这个问题保持着关注;最
业务用例模型 业务用例模型 描述一个业务的流程以及它们与外部各方(如客户和合作伙伴)之间的交互
用例模型的介绍 作者:魏华超 weihuachao@sina.com 用例模型在系统建模过程中是十分重要的,它影响着
用例模型的介绍 作者:魏华超 weihuachao@sina.com 用例模型在系统建模过程中是十分重要的,它影响着
我的基本开发环境是win7 32位,go1.4 windows/386,LiteIDE X 27。其实开发环境不重要 首先我们来安
线程并发的生产者-消费者模型: 1.两个进程对同一个内存资源进行操作,一个是生产者,一个是消费者
FROM:http://www.ibm.com/developerworks/cn/linux/opensource/os-ecemf1/ 2004 年 4 月 01 日 Ecli
版权所有 IT知识库 CopyRight © 2009-2015 IT知识库 IT610.com , All Rights Reserved. 京ICP备09083238号