GoLang Gopher

2019-04-16 language golang

在人类自然语言学界有一个很著名的 “萨丕尔-沃夫” 假说,语言影响或决定人类的思维方式。

Language inuences/determines thought. - Sapir-Whorf hypothesis

那么 Gopher 的价值观是什么。

Overall Simplicity

GoLang 的内部实现很复杂,但是其对外暴露的接口却很简单。

  • 仅有的 25 个关键字,主流编程语言中最少;
  • go 最简单的启动协程方式;
  • gc 减少内存管理的成本;
  • constants 简化常量的定义和使用;
  • interface 纯方法集合,纯行为定义,隐式实现;
  • package 代码组织的唯一形式。

包括代码风格,代码的写法都没有太多的选择,也就是说炫技很难,但是可读性很好。

变量名

通过 gofmt 工具,GoLang 提供了统一的代码风格,而变量的命名在不影响可读的情况下尽量短,例如。

for i, v := range s
t := time.Now()
b := new(bytes.Buffer)

最小化思维

所有的代码、工具都是尽量简单。

循环

只提供了一个 for 循环,其它语言里的 while do while 都可以通过该语句实现。

for i := 0; i < cnt; i++ {}   // normal loop
for i, v := range f.Value {}  // iterator loop
for {}                        // dead loop
for COND {}                   // while
for {                         // do while
	if COND {
		break
	}
}

常量

常量只是数字,可以是整型或者浮点型,无需显示指定类型 (例如U UL),会自动判断。

const Pi = 3.1415
const (
	MaxFrameSize = 1 << 14
	MinFrameSize = 0
)
seconds := time.Nanoseconds()/1e9  // int64

Preference in Concurrency

通过 interface 决定了程序的静态结构,而并发则影响着程序的运行。Go 提供了三种并发原语:

  • 通过协程并发,内部实现了调度单元,对外暴露的却是同步接口;
  • 使用管道进行通讯,利用管道将多个协程组合到一起;
  • select使得协程可以处理多个管道操作。

在设计程序时,需要识别分解出独立的计算单元,并添加到协程中执行,各个协程之间通过管道和 select 建立联系。

下面从各个协程之间的联系,介绍一些常见的模式。

Detached

此时与父进程没有关系,一般其生命周期与进程相同,会执行一些特定的任务,例如监控。

package main

import (
        "fmt"
        "time"
)

func main() {
        go func() {
                ticker := time.NewTicker(time.Second * 1)
                for range ticker.C {
                        fmt.Println(time.Now().Format("2006-01-02 15:04:05"))
                }
        }()

        time.Sleep(time.Duration(3) * time.Second)
}

其实现通常采用 for select 方式,然后通过 timer 或者 event 驱动。

Parent Child

父进程需要等待子进程的退出,并做一些资源的清理。

如果只需要等待一个 child ,可以使用如下方式。

package main

import (
        "fmt"
        "time"
)

func HandleMessage(msgCh <-chan string, quit <-chan bool) {
        for {
                select {
                case m := <-msgCh:
                        fmt.Println(m)
                case <-quit:
                        fmt.Println("Quit now")
                }
        }
}

func main() {
        quit := make(chan bool)
        msgCh := make(chan string)

        go HandleMessage(msgCh, quit)
        for i := 0; i < 3; i++ {
                msgCh <- "Hello World"
        }
        quit <- true // close(quit)
        time.Sleep(time.Duration(100) * time.Microsecond)
}

如果是多个 Children ,那么可以通过关闭管道的方式退出,此时所有的 case <-quit 都会收到 false (实际是零值) 。

当需要获取协程的退出状态时,就需要另外的管道进行通讯。