golang基础学习
接口
-
接口定义
package io type Writer interface { Write(p []byte) (n int, err error) }
-
一个类型如果拥有一个接口需要的所有方法,那么这个类型就实现了这个接口,无需显式实现
-
这一特性有助于在不改变原来类型的前提下抽出公共的部分
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 }
-
类型断言(接口值上的操作。x.(T),这里x表示一个接口的类型和T表示一个类型)
- 断言具体类型 类似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
-
断言接口,改变表述方式,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
-
channel的创建和使用
-
基础使用
```go
ch := make(chan int)//创建
ch <- x // 发送
x = <-ch //接收
<-ch // 没有接收者
ch.close()//关闭
```
-
channel关闭后:发送方发送会panic;接收方不阻塞,将会不停收到零值
var out chan int v, ok:= <- out //当chan关闭,ok会返回false
-
channel的range循环,依次从channel接收数据,当channel被关闭并且没有值可接收时跳出循环
```go
ch := make(chan int)
for x := range ch {
fmt.Print(x)
}
```
-
同步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 }
-
单向channel 只能发送或只能接收的channel,防止对输出队列进行接收操作。常用于函数
var out chan <- int //只能发送 var in <-chan int //只能接受 func test(out chan<- int) { v := 100 out <- v //correct x <- out //compile error }
-
有缓存的channel ch := make(chan int, 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多路复用
- 每一个case代表一个通信操作
- select会等待case中有能够执行的case时去执行
- 每次只会执行一个通信(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:
}
}
}
并发的退出
- 使用一个channel done来广播关闭goroutines
- 不往done写入数据,done close之前,<-done阻塞,cancelled() false
- 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
}
}
数据竞争
并发访问相同变量,且至少其中一个为写操作时发生
避免竞争
- 只读
- 避免多协程访问一个数据(独立/或者顺序访问)
- 允许多协程访问,但不能同时。互斥
其他
slice补充
-
append([]T, T);类似于list.add(T)
-
runes = append(runes, r); 调用方式毫无面向对象的感觉
多返回值
- 不仅是函数支持多返回值
- 类似于map: age, ok := ages[“bob”], chan: x, ok <-chan; 能够直接返回值及是否正常让代码写起来很简洁
结构体匿名成员
-
可实现类似继承的效果
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