Go

Go

go语言学习

Posted by Link on June 7, 2020

golang基础学习

接口

  1. 接口定义

    package io
    
    type Writer interface {
        Write(p []byte) (n int, err error)
    }
    
  2. 一个类型如果拥有一个接口需要的所有方法,那么这个类型就实现了这个接口,无需显式实现

  3. 这一特性有助于在不改变原来类型的前提下抽出公共的部分

    type Audio interface {
      Stream() (io.ReadCloser, error)
      RunningTime() time.Duration
      Format() string // e.g., "MP3", "WAV"
    }
    type Video interface {
      Stream() (io.ReadCloser, error)
      RunningTime() time.Duration
      Format() string // e.g., "MP4", "WMV"
      Resolution() (x, y int)
    }
       
    //在Audio和Video后创建,Audio和Video自动实现Streamer
    type Streamer interface {
      Stream() (io.ReadCloser, error)
      RunningTime() time.Duration
      Format() string
    }
    
  4. 类型断言(接口值上的操作。x.(T),这里x表示一个接口的类型和T表示一个类型)

    1. 断言具体类型 类似instance of
      //panic
      var w io.Writer
      w = os.Stdout
      f := w.(*os.File) // success: f == os.Stdout
      c := w.(*bytes.Buffer) //panic
    
      //返回status
      var w io.Writer = os.Stdout
      f, ok := w.(*os.File) // success: ok, f == os.Stdout
      b, ok := w.(*bytes.Buffer) // failure: !ok, b == nil
    
    1. 断言接口,改变表述方式,w是io.Writer, rw成了io.ReadWriter

      var w io.Writer
      w = os.Stdout //write
      rw := w.(io.ReadWriter) //read write
      

      Goroutines and Channels

Goroutines

  func main() {
    go doSometing()
  }
  func doSometing() {
    // do
  }

channels

  1. channel的创建和使用

  2. 基础使用

```go
ch := make(chan int)//创建
ch <- x // 发送
x = <-ch //接收
<-ch // 没有接收者
ch.close()//关闭
```
  1. channel关闭后:发送方发送会panic;接收方不阻塞,将会不停收到零值

    var out chan int
    v, ok:= <- out //当chan关闭,ok会返回false
    
  2. channel的range循环,依次从channel接收数据,当channel被关闭并且没有值可接收时跳出循环

```go
ch := make(chan int)

for x := range ch {
  fmt.Print(x)
}
```
  1. 同步channel

    channel创建时, 可以指定缓冲大小,当为0时,即为同步channel。

    ch := make(chan int)
    ch := make(chan int, 0)
       
    func main() {
    done := make(chan struct{})
    go func() {
      //...
      done <- struct{}{} // signal the main goroutine
    }()
    <-done // wait for background goroutine to finish
    }
    
  2. 单向channel 只能发送或只能接收的channel,防止对输出队列进行接收操作。常用于函数

    var out chan <- int //只能发送
    var in <-chan int //只能接受
       
    func test(out chan<- int) {
      v := 100
      out <- v //correct
      x <- out //compile error
    }
    
  3. 有缓存的channel ch := make(chan int, 3)类似于队列,在空和满时分别阻塞接收方和发送方

简单并发

  1. 从一些全尺寸图片生成一些缩略图
  2. 每个图片生成缩略图的过程都是独立
  3. 下面是需要在所有图片完成后统计总体大小的函数,需要知道所有goroutines什么时候结束
func makeThumbnails6(filenames <-chan string) int64 {
  sizes := make(chan int64)
  var wg sync.WaitGroup // number of working goroutines
  for f := range filenames {
    wg.Add(1)
    // worker
    go func(f string) {
      defer wg.Done()
      thumb, err := thumbnail.ImageFile(f)
      if err != nil {
        log.Println(err)
        return
    	}
      info, _ := os.Stat(thumb) // OK to ignore error
      sizes <- info.Size()
    }(f)
  }
  // closer
  go func() {
    wg.Wait()
    close(sizes)
  }()
  
  var total int64
  for size := range sizes {
    total += size
  }
  return total
}

select多路复用

  1. 每一个case代表一个通信操作
  2. select会等待case中有能够执行的case时去执行
  3. 每次只会执行一个通信(case)
func main() {
	ch := make(chan int, 1)
	for i := 0; i < 10; i++ {
		select {
		case x := <-ch:
			fmt.Println(x) // "0" "2" "4" "6" "8"
		case ch <- i:
		}
	}
}

并发的退出

  1. 使用一个channel done来广播关闭goroutines
  2. 不往done写入数据,done close之前,<-done阻塞,cancelled() false
  3. done close之后,<-done不阻塞返回零值,cancelled() true
func main() {
	go func() {
		if cancelled() {
			return
		}
		//normal
	}()
}

var done = make(chan struct{})

func cancelled() bool {
	select {
	case <-done:
		return true
	default:
		return false
	}
}

数据竞争

并发访问相同变量,且至少其中一个为写操作时发生

避免竞争

  1. 只读
  2. 避免多协程访问一个数据(独立/或者顺序访问)
  3. 允许多协程访问,但不能同时。互斥

其他

slice补充

  1. append([]T, T);类似于list.add(T)

  2. runes = append(runes, r); 调用方式毫无面向对象的感觉

多返回值

  1. 不仅是函数支持多返回值
  2. 类似于map: age, ok := ages[“bob”], chan: x, ok <-chan; 能够直接返回值及是否正常让代码写起来很简洁

结构体匿名成员

  1. 可实现类似继承的效果

    type Circle struct {
      Point
      Radius int
    }
    type Wheel struct {
      Circle
      Spokes int
    }
       
    //Wheel类型的变量可以直接使用Circle中的Radius,甚至Circle中的Point的X和Y
    w.X = 8 // equivalent to w.Circle.Point.X = 8
    w.Y = 8 // equivalent to w.Circle.Point.Y = 8
    w.Radius = 5 // equivalent to w.Circle.Radius = 5
    w.Spokes = 20