cai_go

TOC TOC

Introduction

it's a course from geektime about golang, provided by a guru named 蔡超

编写第一个Go程序

环境变量GOPATH

  1. 1.8版本前必须设置这个环境变量
  2. 1.8(含)后有默认值:
    1. *nix 默认为 $HOME/go
    2. Windows 默认为 %USERPROFILE%/go

GOPATH 也是 go get 命令下载存储的地方,所以默认也是 $HOME/go

go version

应用程序入口

  1. 必须是 main 包: package main
  2. 必须是 main 方法: func main()
  3. 文件名不一定是 main.go

退出返回值

  1. Go中 main 不支持任何返回值
  2. 通过 os.Exit 来返回状态

获取命令行参数

  1. main 函数不支持传入参数
  2. 通过 os.Args 获取 第0项是二进制文件名,和c系语言一致

变量&常量

编写测试程序 [0/1]

  1. 源码文件以 _test 结尾: xxx_test.go

  2. 测试方法名以 Test 开头: func TestXXX(t *testing.T) {...} (大写的函数名表示包外可访问,虽然测试例实际无此需求)

  3. [ ] package的目录名和package名一致应该是convention,就像java里一样 这块等到 package 再看一下

    $ tree
    .
    ├── go_learning.iml
    └── src
        ├── ch01
        │   └── main
        │       └── hello_world.go
        └── ch2
            ├── fib
            │   └── fib_test.go
            └── test
                └── try_test.go
    

    hello_world.go : package main fib_test.go : package fib try_test.go : package try_test

变量

var a int
a := 2

常量

快速常量赋值

const (
    Monday = iota + 1
    Tuesday 
)

留意下 iota 就好

数据类型

基本数据类型

bool
string
int int8 int16 int32 int64
unit uint8 uint16 uint32 uint64 uintptr
byte // alias  for uint8
rune // alias for int32, represents a Unicode code point
float32 float64
complex64 complex128

类型转换

  1. 不允许隐式类型转换
  2. 别名和原有类型也不允许隐式类型转换

类型的预定义值

  1. math.MaxInt64
  2. math.MaxFloat64
  3. math.MaxUint32

指针类型

  1. 不支持指针运算
  2. string 是值类型,其默认的初始化值为空字符串,而不是 nil
  3. 个人看法:其实就是可为空的引用

运算符

算术运算符

  1. 没有前置 ++ --

== 比较数组

  1. 可以比较,严格相等才为 true

位运算符

&^ 按位置零

右边如果是1,则无论左边是啥都置位0; 否则就是左边的值。

1 &^ 0 -- 1
1 &^ 1 -- 0
0 &^ 1 -- 0
0 &^ 0 -- 0
就是普通的 a & (^b)

条件和循环

数组和切片

数组的声明

package array_test

import "testing"

func TestArrayInit(t *testing.T) {
    var arr [3]int
    arr1 := [4]int{1,2,3,4}
    arr3 := [...]int{1,2,3,5}
    arr[1] = 5
    t.Log(arr[1], arr[2])
    t.Log(arr1, arr3)
}

func TestArrayTravel(t *testing.T) {
    arr3 := [...]int{1, 3, 4, 5}
    for i := 0; i < len(arr3); i++ {
        t.Log(arr3[i])
    }
    for _, e := range arr3 {
        t.Log(e)
    }

}

这里 ... 也是给数组用的,slice方括号里没有任何东西

数组截取

和python的语法一样的

切片内部结构

就是 c++ 里的vector,java里的arraylist,没啥好说的

map

声明

m1 := map[int]int{1:1,2:2,3:9}
t.Log(m1[2])
t.Logf("len m1=%d", len(m1))
m2 := map[int]int{}
m2[4] = 16
t.Logf("len m2=%d", len(m2))
m3 := make(map[int]int, 10)
t.Logf("len m3=%d", len(m3))
// cannot use map
// t.Logf("len m3=%d", cap(m3))

缺失值

遍历

还是用range

Map与工厂模式

字符串

go里的函数

可变长参数

defer函数

行为的定义和实现

结构体定义

形如

type Employee struct {
  Id string
  Name string
  Age int
}

实例创建及初始化

e := Employee {"0", "Bob", 20}
e1 := Employee {Name: "Mike", Age: 30}
e2 := new(Employee) // 此处返回的是指针
e2.Id = "2"
e2.Age = 22

行为(方法)定义

receiver 是指针时无需拷贝

Go语言的相关接口

Duck Type式接口实现

接口变量

扩展与复用

type Dog struct {
    Pet
}

<- 匿名成员,所有方法自动委托 https://www.google.com/search?q=go+anonymous+field

不一样的接口类型,一样的多态

example:

package poly

import (
    "fmt"
    "testing"
)

type Code string

type Programmer interface {
    WriteHelloWorld() Code
}

type GoProgrammer struct {

}

func (p *GoProgrammer) WriteHelloWorld() Code {
    return "fmt.Println(\"Hello World!\")"
}

type JavaProgrammer struct {

}

func (p *JavaProgrammer) WriteHelloWorld() Code {
    return "System.out.Println(\"Hello World!\")"
}

func writeFirstProgram(p Programmer) {
    fmt.Println("%T %v\n", p, p.WriteHelloWorld())
}

func TestPolymorphism(t *testing.T) {
    goProg := new(GoProgrammer)
    javaProg := new(JavaProgrammer)
    writeFirstProgram(goProg)
    writeFirstProgram(javaProg)
}

空接口

interface{}

Go语言错误机制

panic and recover

package/module

for current go learning, setup GOPATH

export GOPATH=$HOME/go_learning:$GOPATH

beware we have a src subdir

$ tree go_learning
go_learning
├── go_learning.iml
└── src
     ── ch01
        └── main
            └── hello_world.go

for idea, we can setup via

Settings / Languages & Frameworks / Go / GOPATH

导入路径从 src 后开始写,例如:

package client

import (
    "ch15/series"
    "testing"
)

func TestPackage(t *testing.T) {
    t.Log(series.GetFibonacci(5))
}

init方法

package

依赖管理

Go未解决的依赖问题

常用的依赖管理工具

godep
https://github.com/tools/godep 早期
glide
https://github.com/Masterminds/glide
dep
https://github.com/golang/dep 比较新

Vendor路径

resolve 顺序如下:

  1. 当前包下的vendor目录
  2. 向上级目录查找,知道找到 src 下的vendor目录
  3. 在GOPATH下面查找依赖包
  4. 在GOROOT目录下查找依赖包

glide使用实例

安装可以参考repo说明,windwos 也阔以 go get github.com/Masterminds/glide

logs example:

# glide init
[INFO]  Generating a YAML configuration file and guessing the dependencies
[INFO]  Attempting to import from other package managers (use --skip-import to skip)
[INFO]  Scanning code to look for dependencies
[INFO]  --> Found test reference to github.com\easierway\concurrent_map
[INFO]  Writing configuration file (glide.yaml)
[INFO]  Would you like Glide to help you find ways to improve your glide.yaml configuration?
[INFO]  If you want to revisit this step you can use the config-wizard command at any time.
[INFO]  Yes (Y) or No (N)?
Y
[INFO]  Looking for dependencies to make suggestions on
[INFO]  --> Scanning for dependencies not using version ranges
[INFO]  --> Scanning for dependencies using commit ids
[INFO]  Gathering information on each dependency
[INFO]  --> This may take a moment. Especially on a codebase with many dependencies
[INFO]  --> Gathering release information for dependencies
[INFO]  --> Looking for dependency imports where versions are commit ids
[INFO]  Here are some suggestions...
[INFO]  The package github.com/easierway/concurrent_map appears to have Semantic Version releases (http://semver.org).

[INFO]  The latest release is v1.0.0. You are currently not using a release. Would you like
[INFO]  to use this release? Yes (Y) or No (N)
Y
[INFO]  Would you like to remember the previous decision and apply it to future
[INFO]  dependencies? Yes (Y) or No (N)
N
[INFO]  Updating github.com/easierway/concurrent_map to use the release v1.0.0 instead of no release
[INFO]  The package github.com/easierway/concurrent_map appears to use semantic versions (http://semver.org).
[INFO]  Would you like to track the latest minor or patch releases (major.minor.patch)?
[INFO]  The choices are:
[INFO]   - Tracking minor version releases would use '>= 1.0.0, < 2.0.0' ('^1.0.0')
[INFO]   - Tracking patch version releases would use '>= 1.0.0, < 1.1.0' ('~1.0.0')
[INFO]   - Skip using ranges
[INFO]  For more information on Glide versions and ranges see https://glide.sh/docs/versions
[INFO]  Minor (M), Patch (P), or Skip Ranges (S)?
S
[INFO]  Would you like to remember the previous decision and apply it to future
[INFO]  dependencies? Yes (Y) or No (N)
N
[INFO]  Configuration changes have been made. Would you like to write these
[INFO]  changes to your configuration file? Yes (Y) or No (N)
Y
[INFO]  Writing updates to configuration file (glide.yaml)
[INFO]  You can now edit the glide.yaml file.:
[INFO]  --> For more information on versions and ranges see https://glide.sh/docs/versions/
[INFO]  --> For details on additional metadata see https://glide.sh/docs/glide.yaml/

然后 glide install 后会有 vendor 路径

nevermind,intellij idea/goland 里面集成的是 dep,这块先这么过去就好了

协程机制

Thread vs. Goroutine

共享内存并发控制

Example:

package share_mem

import (
    "sync"
    "testing"
)

func TestCounter(t *testing.T) {
    var mut sync.Mutex
    var wg sync.WaitGroup
    couter := 0
    for i := 0; i < 5000; i++ {
        wg.Add(1)
        go func() {
            defer func() {
                mut.Unlock()
            }()
            mut.Lock()
            couter ++
            wg.Done()
        }()
    }
    // time.Sleep(1 * time.Second)
    wg.Wait()
    t.Logf("Counter = %d", couter)
}

CSP并发机制

多路选择和超时控制

select

channel的关闭

任务的取消

通过关闭 channel 来做任务取消是阔以的:

select {
case <- ch:
    return true
default:
    return false
}  

close(ch) 的时候,所有receiver都会拿到一个message

ps 除开 interface{} 也有 struct{} ,实例化方式是 struct{}{}

Context与任务取消

根context
通过 context.Background() 创建
子context

context.WithCancel(parentContext) 创建

  • ctx, cancel := context.WithCancel(context.Background())

具体再看下context的文档

单例可以使用 sync.once

仅需任意任务完成

maybe goroutine leak use chan with buffer can avoid this, but it should be someway to do it with sync chan too

等待所有任务with csp/channel

sync.Pool

sync.Pool对象获取

使用sync.Pool

pool := &sync.Pool {
    New: func() interface{} {
        return 0
    },
    }
arr := pool.Get().(int)
pool.Put(10)

sync.Pool对象生命周期

sync.Pool 总结

单元测试

内置单元测试框架

代码覆盖率

go test -v -cover

断言

https://github.com/stretchr/testify

benchmark

函数以 Benchmark 开头 示例:


func BenchmarkConcatStringByBytesBuffer(b *testing.B) {
    elems := []string{"1", "2", "3", "4", "5"}
    b.StartTimer()
    for i := 0; i < b.N; i++ {
        var buf bytes.Buffer
        for _, elem := range elems {
            buf.WriteString(elem)
        }
    }
    b.StopTimer()
}

func BenchmarkConcatStringByAdd(b *testing.B) {
    elems := []string{"1", "2", "3", "4", "5"}
    b.ResetTimer()
    for i := 0; i < b.N; i++ {
        ret := ""
        for _, elem := range elems {
            ret += elem
        }
    }
    b.StopTimer()
}