Go学习笔记(七)

240 阅读5分钟

(文章持续更新中...有志同道合的人可以一起探讨下。整个系列(1~8)准备重写,不过都是很基础的入门笔记,想学到很深入的东西则不建议阅读...)

接口是双方约定的一种合作协议。每个接口类型由数个方法组成

type 接口类型名 interface{
    方法名1( 参数列表1 ) 返回值列表1
    方法名2( 参数列表2 ) 返回值列表2
    …
}

type Writer interface {
    Write(p []byte) (n int, err error)
}
  • 接口的方法与实现接口的类型方法格式一致.实现接口类型中的方法的名称、参数列表、返回参数列表中的任意一项与接口要实现的方法不一致,那么接口的这个方法就不会被实现。
  • 接口中所有方法均被实现.当一个接口中有多个方法时,只有这些方法都被实现了,接口才能被正确编译并使用

Go 语言的源码复用建立在包(package)基础之上.Go 语言的入口 main() 函数所在的包(package)叫 main,main 包想要引用别的代码,必须同样以包的方式进行引用,Go 语言的包与文件夹一一对应,所有与包相关的操作,必须依赖于工作目录(GOPATH)

GOPATH 绝对路径提供项目的工作目录

goroutine 的概念类似于线程,但 goroutine 由 Go 程序运行时的调度和管理。Go 程序会智能地将 goroutine 中的任务合理地分配给每个 CPU

Go 程序中使用 go 关键字为一个函数创建一个 goroutine。一个函数可以被创建多个 goroutine,一个 goroutine 必定对应一个函数

// 为普通函数创建goroutine 的格式
go 函数名( 参数列表 )

如果需要在 goroutine 中返回数, 用通道(channel)把数据从 goroutine 中作为返回值传出

package main

import (
    "fmt"
    "time"
)

func running() {
    var times int
    for {
        times++
        fmt.Println("tick", times)
        time.Sleep(time.Second)
    }
}

func main() {
    // 并发执行程序
    go running()
    // 接受命令行输入, 不做任何事情
    var input string
    fmt.Scanln(&input)
}

一般情况下,可以使用 runtime.NumCPU() 查询 CPU 数量,并使用 runtime.GOMAXPROCS() 函数进行设置

runtime.GOMAXPROCS(runtime.NumCPU())

并发和并行的概念

并发:任务在不同的时间点在处理器进行处理。在同一时间点,任务并不会同时运行 并行:每一个任务分配给每一个处理器独立完成。在同一时间点,任务一定是同时运行

Go语言通道

单纯地将函数并发执行是没有意义的。函数与函数间需要交换数据才能体现并发执行函数的意义

任何时候,同时只能有一个 goroutine 访问通道(channel Go语言中一种特殊类型)进行发送和获取数据。(类似队列,先进先出

// 申明通道类型
var 通道变量 chan 通道类型(通道内的数据类型)

chan 类型的空值是 nil,声明后需要配合 make 后才能使用

通道是引用类型,需要使用 make 进行创建

通道实例 := make(chan 数据类型)

ch1 := make(chan int)                 // 创建一个整型类型的通道
ch2 := make(chan interface{})         // 创建一个空接口类型的通道, 可以存放任意格式
type Equip struct{ /* 一些字段 */ }
ch2 := make(chan *Equip)             // 创建Equip指针类型的通道, 可以存放*Equip

通道创建后,就可以使用通道进行发送和接收操作

通道的发送使用特殊的操作符<-

// 数据通过通道发送数据格式
通道变量 <- 值(值的类型必须与ch通道的元素类型一致)

// 创建一个空接口通道
ch := make(chan interface{})
// 将0放入通道中
ch <- 0
// 将hello字符串放入通道中
ch <- "hello"

发送将持续阻塞直到数据被接收

把数据往通道中发送时,如果接收方一直都没有接收,那么发送操作将持续阻塞。Go 程序运行时能智能地发现一些永远无法发送成功的语句并做出提示,代码如下:

package main
func main() {
    // 创建一个整型通道
    ch := make(chan int)
    // 尝试将0通过通道发送
    ch <- 0
}

通道接收同样使用<-操作符

通道的收发操作在不同的两个 goroutine 间进行

接收将持续阻塞直到发送方发送数据

通道一次只能接收一个数据元素

阻塞模式接收数据时,将接收变量作为<-操作符的左值,执行该语句时将会阻塞,直到接收到数据并赋值给 data 变量

// 阻塞式接收值
data := <-ch

// 非阻塞式
data, ok := <-ch
data 表示接收到的数据
ok   表示是否接收到数据

非阻塞的通道接收方法可能造成高的 CPU 占用,因此使用非常少

// 忽略从通道返回的数据
<-ch

Go 的通道可以在声明时约束其操作方向,如只发送或是只接收。这种被约束方向的通道被称做单向通道

只能发送的通道类型为chan<-,只能接收的通道类型为<-chan

var 通道实例(声明的通道变量) chan<- 元素类型    // 只能发送
var 通道实例(声明的通道变量) <-chan 元素类型    // 只能接收

使用 close() 来关闭一个通道.给被关闭通道发送数据将会触发panic

close(ch)

互斥锁是一种常用的控制共享资源访问的方法,它能够保证同时只有一个 goroutine 可以访问共享资源

Lock()

UnLock()