Go in Action - Manning 2016(读书笔记)

Go in Action

Go in Action - Manning 2016

目录

  • 1 介绍Go
  • 2 Go快速入门
  • 3 打包、工具
  • 4 数组、切片和maps
  • 5 Go的类型系统
  • 6 并发
  • 7 并发模式
  • 8 标准库
  • 9 测试与性能基准

介绍Go

  1. fmt.Println("Hello, world")
  2. ?这本书里似乎没提到Go异常处理?

Go快速入门

  1. log.SetOutput(os.Stdout) //import ("log" ...)
  2. package main //main必须在main包里才会生成可执行文件? 通常以所在目录命名?
    1. func init() { ... }
    2. func main() { ... }
  3. search.go
    1. var matchers = make(map[string]Matcher)
    2. go func(matcher Matcher, feed *Feed) { ... }(matcher, feed)
  4. defer file.Close() //跟初始化file, err := os.Open(dataFile)在同一个作用域,确实比try-finally或者using要简单一点
  5. func Match(matcher Matcher, feed *Feed, searchTerm string, results chan<- *Result) {
    1. 注意这里‘写chan’的参数声明
  6. 如果2个value struct里的*成员引用同一个对象?——会自动引用计数吗?
  7. XMLName xml.Name `xml:"item"` //还有这种类型?

打包、工具

  1. 远程导入:
    1. import "github.com/spf13/viper" //go build命令会搜索本地GOPATH(嗯?这会导致总是下载到最新的版本?有可能不兼容)
  2. 命名导入:
    1. import (myfmt "mylib/fmt")
  3. 匿名导入(sql驱动,略)
  4. filename := os.Args[1]
    1. contents, err := ioutil.ReadFile(filename) //读全部内容?没有迭代器吗
  5. go build .
  6. go vet //lint工具?
  7. go fmt //代码洁癖...
  8. godoc -http=:6060
  9. 依赖管理
    1. vendoring
      1. import "bitbucket.org/ww/goautoneg" 转换为
        • "github.ardanstudios.com/myproject/Godeps/_workspace/src/bitbucket.org/ww/goautoneg"
    2. gb
      1. import的问题是没有指定版本
      2. vendored code:$PROJECT/vendor/src/
      3. 与go get不兼容
      4. gb build all

数组、切片和maps

  1. 数组
    1. array := [5]int{10, 20, 30, 40, 50}
    2. array := [...]int{10, 20, 30, 40, 50} //自动推断数组长度,维度一旦初始化不可修改
    3. array := [5]int{1: 10, 2: 20} //选择下标初始化?
    4. array[2] = 35
    5. array := [5]*int{0: new(int), 1: new(int)} //指针数组
      1. *array[0] = 10 //其实Go语言里并不严格区分指针和引用?
    6. array1 = array2 //数组是value对象,赋值操作将导致内存copy
    7. array2d := [4][2]int{{10, 11}, {20, 21}, {30, 31}, {40, 41}}
    8. 传递数组的地址给函数:func foo(array *[1e6]int) { ... } //注意,这里的数组指针类型比C语言要直接多了
  2. slice:是对array的封装,内部提供了addr,length,capacity元数据
    1. slice := make([]string, 5)
    2. slice := make([]int, 3, 5)
    3. slice := []string{"Red", "Blue", "Green", "Yellow", "Pink"} //注意这里[]里既没有长度常量,也没有...自动推断
    4. newSlice := slice[1:3] //长度2,容量4?newSlice初始情况下引用slice?
      1. slice容量=5,newSlice的容量=5-1=4,注意,newSlice不可以访问slice[0]元素(这种设计很有意思!)
    5. 多个slice可能共享一块内存(那么修改需要同步吗)
    6. slice[index]只能访问length范围内的元素,要访问容量范围内的,需要append:newSlice = append(newSlice, 60)
      1. 当底下的实际存储不足新capacity时,append会分配新内存?有点realloc的味道
      2. append:length=1000一下时每次double,否则增加25%
    7. slice := source[2:3:4] //如果是start:length:capacity三元组(不是Python里面的那种语法)
    8. 迭代:for index, value := range slice { ... } //Go惯用法;想起了Python里的enumerate函数
  3. map
    1. dict := make(map[string]int)
    2. dict := map[string]string{"Red": "#da1337", "Orange": "#e95a22"} //注意,:=代表类型定义(自动推断)及初始化
    3. dict := map[[]string]int{} //编译错误:slice不能用作map key(必须可执行==操作),但可用作map value
    4. nil map问题:
      1. var colors map[string]string
      2. colors["Red"] = "#da1337" //运行时错误
        1. 可以这样:value, exists := colors["Blue"] //又是Go惯用法
      3. value := colors["Blue"] //这样是可以的,如果key不存在,value返回类型的零值
    5. delete(colors, "Coral") //删除元素,又是内置函数,这一点不同于C++语言的成员函数风格
    6. map在函数之间传递时不会copy

Go的类型系统

  1. var总是初始化变量为零值
    1.  := 同时声明及初始化
  2. type typeName struct { fields... }
    1. 然后就可以func (a typeName) methodName(args) retType { ... }扩展方法了
      1. 也可以写成func (a *typeName) ...,但是调用方法时全部是.号,但是指针类型的变量必须是&struct{...}初始化来的
      2. a typeName的形参代表方法操作的是type的value类型,也就是C++里面的const方法?
      3. value类型的变量可以调用用指针类型定义的方法(这些地方太容易让人犯迷糊了),将会被转换为(&obj).method()
  3. 引用类型:slice、map、channel、interface、func
    1. `header` value, contains a pointer
  4. Interfaces
    1. `method sets`(C++里的vtable?)
    2. 接口里的方法指针是不是运行时动态填充的???
    3. 接口实现的接收者通常是*T,而不是value类型的T?
      1. 如果你以value T类型作为接受者来实现接口,那么接口作为参数时可以接受T和*T(?)
  5. 类型嵌入
    1. 有点Ruby/Scala mixin的风格~
    2. inner type promotion //只要名字不冲突?
  6. Exporting and unexporting identifiers
    1. 取决于名字的第1个字母是大写还是小写。。。
  • 感觉本章里作者说的废话太多了,其实简单一句话就可以说清楚了!

并发

  1. logical processors?
    1. 当执行blocking IO操作时,detach;当IO ready时,重新attach?
  2. 第1个例子:
    1. runtime.GOMAXPROCS(1) //或runtime.NumCPU()
    2. var wg sync.WaitGroup //wg的用法是不是可以直接看文档和sample代码即时掌握?
    3. wg.Add(2)
    4. go func() {
      defer wg.Done()
      }()
    5. wg.Wait()
  3. scheduler可在goroutines执行时自动打断?(swap out?抢占式调度?)这个内部操作发生在fmt.Printf调用的时候吗
  4. 竞争条件
    1. 读写共享变量必须是atomic的;
    2. 检测工具:go build -race
    3. 方法1:atomic.AddInt64(&counter, 1) #奇怪,atomic包是怎么做到对一个int64变量的修改是原子的?利用平台特定的汇编指令?
    4. 方法2:mutex sync.Mutex => mutex.Lock() { ... } mutex.Unlock()
  5. 更灵活地,Channels
    1. 初始化:
      1. unbuffered := make(chan int) #如果有一个没准备好,第一个操作(发送/接受)wait
      2. buffered := make(chan string, 10)
        1. close操作:仍然可以接受,但不可以再发送(任务派发端)
    2. 发送数据:buffered <- "Gopher"
    3. 接受数据:value := <-buffered
  6. rand.Seed(time.Now().UnixNano())

并发模式

  1. Runner:监控运行时间及超时终止
    1. type Runner struct {
      1. interrupt chan os.Signal
      2. complete chan error
      3. timeout <-chan time.Time
      4. tasks []func(int)
    2. var ErrTimeout = errors.New("received timeout") //预创建的错误对象
    3. func (r *Runner) Add(tasks ...func(int)) { r.tasks = append(r.tasks, tasks...) } //注意这里的变参;
    4. func (r *Runner) Start() error { //运行所有tasks,并监控超时错误
      1. signal.Notify(r.interrupt, os.Interrupt)
      2. go func() { r.complete <- r.run() }()
      3. select {
        1. case err := <-r.complete: return err
        2. case <-r.timeout: return ErrTimeout
    5. func (r *Runner) run() error { ... } //依次执行每个task,略
    6. func (r *Runner) gotInterrupt() bool {
      1. select {
        1. case <-r.interrupt: signal.Stop(r.interrupt) return true //注意这里检查有没有接受到操作系统中断
        2. default: return false //default分支使得前面的<-r.interrupt操作不会阻塞(?有可能丢失中断吗)
          • 注意,初始化:interrupt: make(chan os.Signal, 1), timeout: time.After(duration), //把OS中断和超时建模为特殊的chan
  2. Pooling
    1. type Pool struct {
      1. m sync.Mutex //这个用来保护什么?chan本身不是自动同步的嘛(保护对closed变量的赋值???靠)
      2. resources chan io.Closer
      3. factory func() (io.Closer, error) //这个命名真恶心,应该改成allocNewResource
      4. closed bool
    2. func (p *Pool) Acquire() (io.Closer, error) {
      1. select {
        1. case r, ok := <-p.resources: ...
        2. default: return p.factory()
    3. func (p *Pool) Release(r io.Closer) {
      1. p.m.Lock()
      2. defer p.m.Unlock()
      3. ...
      4. select { case p.resources <- r: ... //default:pool已满,直接释放资源
    4. 循环内启动goroutine防止共享同一个index/counter变量:
      1. go func(a int){ ... }(i)
  3. Work
    1. 命名有点问题,Work()似乎应该是ExecuteWork()?

标准库

  1. http://sourcegraph.com/code.google.com/p/go/.GoPackage/io/.def/Writer 靠,无法访问
  2. Logging
    1. log.SetPrefix("TRACE: ")
    2. log.SetFlags(log.Ldate | log.Lmicroseconds | log.Llongfile)
    3. log.Println/Fatalln/Panicln("message")
    4. const {
      1. Ldate = 1 << iota
    5. 定制logger:
      1. var ( Trace *log.Logger ...... )
      2. Trace = log.New(ioutil.Discard, "TRACE: ", log.Ldate|log.Ltime|log.Lshortfile) # var Discard io.Writer = devNull(0)
    6. Stdin = NewFile(uintptr(syscall.Stdin), "/dev/stdin") //?
    7. io.MultiWriter(file, os.Stderr) //?
  3. Encoding/Decoding
    1. gResult struct {
      1. GsearchResultClass string `json:"GsearchResultClass"` //struct内的成员需要特殊的json映射声明(tags元数据);
      2. ...
    2. gResponse struct {
      ResponseData struct {
      Results []gResult `json:"results"`
      } `json:"responseData"`
    3. 解码:
      1. var gr gResponse
      2. err = json.NewDecoder(resp.Body).Decode(&gr)
    4. unmarshal:
      1. err := json.Unmarshal([]byte(JSON), &c) //?
      • 更灵活的解析:?
        var c map[string]interface{}
        err := json.Unmarshal([]byte(JSON), &c) //这种情况下感觉代码中 interface{}会被滥用?
    5. 编码
      1. c := make(map[string]interface{})
      2. ...
      3. data, err := json.MarshalIndent(c, "", " ")
  4. Input and output
    1. 好像没什么特别值得一说的(又一套API命名风格)

测试与性能基准

  1. listing01_test.go
    1. func TestDownload(t *testing.T) {
      t.Log("Given the need to test downloading content.") #t.Errorf代表测试失败?
      ...
      resp, err := http.Get(url)
      defer resp.Body.Close()
  2. func SendJSON(rw http.ResponseWriter, r *http.Request) {
    1. u = ...
    2. rw.Header().Set("Content-Type", "application/json")
    3. rw.WriteHeader(200)
    4. json.NewEncoder(rw).Encode(&u)
  3.  ?
    1. r, _ := http.NewRequest("GET", "/sendjson", nil)
    2. rw := httptest.NewRecorder()
    3. http.DefaultServeMux.ServeHTTP(rw, r)
  4. 性能基准测试
    1. func BenchmarkSprintf(b *testing.B) {
      b.ResetTimer()
    2. go test -v -run="none" -bench="BenchmarkSprintf" //运行次数?

你可能感兴趣的:(读书笔记)