golang核心笔记
Go的编译命令
- GOROOT: go当前安装目录
- GOPATH: 工作区的集合,多个用:分隔.工作区是放置 Go 源码文件的目录.三个目录:**src 目录,pkg 目录,bin 目录**。
- GO111MODULE: mod包管理开启
- GOPROXY: go代理,配合GO111MODULE使用
- go env -w GO111MODULE=on
- eport GO111MODULE=auto
- go mod init [project_name] # 初始化一个mod管理项目
- go env # 查看更多命令
- go build xx.go -o xx -i [build flags]
- go run xx.go
- go clean # 删除目标文件和缓存文件
- go test -v # 测试项目目标的xxx_test.go文件
- go test -test.bench=".*" # 测试总个目录的
- go test xxx_test.go -test.bench=".*" # 测试单个文件
- go test xxx_test.go -benchmem -test.bench=".*" # 显示内存
- gofmt -w xx.go # 格式化某个文件或目录
变量
const name = 'ok' //隐式类型定义
const name string = "ok" //显式类型定义
var dp = [3][5]int{[5]int{1, 2, 3, 4, 5}, [5]int{4, 5, 6}, [5]int{7, 8, 9}} //多维数组的初始化,不足补0
s := []string{"abc", "ABC"} //切片初始化
slice1 := s[1:2] //[开始:结束] 开始和结束不能大于数组实际长度
var slice2 []type = make([]type, len)
s1 := []int{1, 2} //切片增加元素
s1 = append(s1, 3)
s2 := []int{4, 5}
s3 := append(s1, s2...) //合并两个切片
//**************************************************************************
res := struct { //匿名结构体
Name string
Age int
}{Name:"lily", Age:18}
jsons, err := json.Marshall(res) //json序列化(注:jsons的类型是[]type)
errs = json.Unmarshal(jsons, &res2) //反序列化
//**************************************************************************
person := map[int]string{ //map集合是无序的 key-value 数据结构
1 : "Tom",
2 : "Aaron",
3 : "John",
}
delete(person, 2) //删除
person[2] = "Jack" //增加
person[3] = "Kevin" //修改
slice切片:是对数组一个连续片段的引用,是一种引用类型
- 切片提供了一个与指向数组的动态窗口
- Go 中数组赋值和函数传参都是值复制的,而切片是引用传递的
- 使用make创建切片:实现运行期动态指定数组长度,绕开编译期常量的限制
- 切片扩容策略:小于1024个元素,翻倍扩容。超过1024后1.25倍扩容。
// 切片定义
type slice struct {
array unsafe.Pointer
len int
cap int // cap一定要比len大
}
// 从 slice 中得到一块内存地址
s := make([]byte, 200)
ptr := unsafe.Pointer(s[0])
// 从go的内存地址中构造一个slice
var ptr unsafe.Pointer
var s1 = struct { //初始化一个结构体
addr unitptr
len int
cap int
}{ptr, length, length}
s := *(*[]byte)(unsafe.Pointer(s1)) // ptr 转 slice
流程控制
import (
"crypto/md5"
"encoding/hex"
"fmt"
"time"
)
func MD5(str string) string {
s := md5.New()
s.Write([]byte(str))
return hex.EncodeToString(s.Sum(nil))
}
func main() {
switch var {
case val1:
fmt.Println(time.Now().Format(`2006-01-02 15:04:05`)) //获取系统当前时间
case val2:
MD5("md5sum")
default:
time.Sleep(1*time.Second)
}
}
for-range //迭代语法:可用于array、slice、map、chan
interface{}与类型断言
interface{} 只定义方法
package main
type interfaceName interface {
}
- 接口实现:接口的方法与实现接口的类型方法格式一致(方法名、参数类型、返回值类型一致)。所有方法都要被实现。
- 接口赋值:接口本质上是一个指针类型。实现了该接口的struct和子接口,可以赋值给接口。
- 接口类型做为参数:如果一个函数有个接口作为参数。那么实现了该接口的struct都可以做为此参数。
- 空接口: Go也能像其它动态语言一样,在数据结构中存储任意类型的数据。
package main
import "fmt"
func main() {
any := make([]interface{}, 5)
any[0] = 11
any[1] = "hello world"
any[2] = []int{11, 22, 33, 44}
for _, value := range any {
fmt.Println(value)
}
}
- 接口嵌套:内部属性属于外部属性-
类型断言
- 如果不清楚当前struct是什么类型,可以采用类型断言,运行时判断。
package main
func main() {
switch t := areaIntf.(type) {
case *Rectangle:
// do something
case *Triangle:
// do something
default:
// do something
}
}
多态
- 1、多个类型(结构体)可以实现同一个接口。
- 2、一个类型(结构体)可以实现多个接口。
- 3、实现接口的类(结构体)可以赋值给接口。
package main
import "fmt"
type Shaper interface {
Area() float64
}
type Rectangle struct {
length float64
width float64
}
func (r *Rectangle) Area() float64 {
return r.length * r.width // 实现 Shaper 接口中的方法
}
func (r *Rectangle) Set(l float64, w float64) {
r.length = l //Set 是属于 Rectangle 自己的方法
r.width = w
}
type Triangle struct { // ==== Triangle ====
bottom float64
hight float64
}
func (t *Triangle) Area() float64 {
return t.bottom * t.hight / 2
}
func (t *Triangle) Set(b float64, h float64) {
t.bottom = b
t.hight = h
}
func main() {
rect := new(Rectangle)
rect.Set(2, 3)
areaIntf := Shaper(rect) //这种方法只能将指针类型的类示例赋值给接口
fmt.Printf("The rect has area: %f\n", areaIntf.Area())
triangle := new(Triangle)
triangle.Set(2, 3)
areaIntf = Shaper(triangle) //这种方法只能将指针类型的类示例赋值给接口
fmt.Printf("The triangle has area: %f\n", areaIntf.Area())
}
GO配置文件读取
- 内置flag包获取配置
- config.json文件、ini文件、yaml文件、toml文件
- 万能的viper
package main
//json文件
import "encoding/json"
buf, _ := ioutil.ReadFile(path)
myConfig := &MyConfig{} //对应json中的k-v项
_ = json.Unmarshal(buf, myConfig)
// ini文件
import "github.com/go-ini/ini"
myConfig := &MyConfig{} //对应ini中的k-v项
err := ini.MapTo(myConfig, "config.ini")
// 另一种常用来读ini文件:
import "gopkg.in/ini.v1"
cfg,_ := ini.Load(path)
cfg.Section("").Key("app_mode").String() //read
cfg.Section("section_name").Key("port").SetValue("8086") //write
cfg.SaveTo(path)
// yaml文件
import "gopkg.in/yaml.v2"
myConfig := &MyConfig{} //对应ini中的k-v项
file, err := ioutil.ReadFile("config.yaml")
err = yaml.Unmarshal(file, myConfig)
// toml文件
import "github.com/BurntSushi/toml"
myConfig := &MyConfig{} //对应yaml中的k-v项
toml.DecodeFile("config.toml", myConfig)
GO并发
- 并发主要由切换时间片来实现<同时>运行。
- 并行是直接利用多核实现多线程的运行。
- goroutine 奉行通过
通信来共享内存
(CSP并发模型),而不是共享内存来通信
Goroutine启动一个协程:(如果main协程退出,case协程自动退出)
package main
func case() {
fmt.Println("case Goroutine!")
}
func main() {
go case() // 启动另外一个goroutine去执行case函数
fmt.Println("main协程 done!")
}
使用sync.WaitGroup启动多个协程
package main
import "sync"
import "fmt"
var wg sync.WaitGroup
func hello(i int) {
defer wg.Done() // goroutine结束就登记-1
fmt.Println("Hello Goroutine!", i)
}
func main() {
for i := 0; i < 10; i++ {
wg.Add(1) // 启动一个goroutine就登记+1
go hello(i)
}
wg.Wait() // 等待所有登记的goroutine都结束
}
Goroutineg与线程
- OS线程一般都有固定的栈内存(一般2MB)
- goroutine(可增长的栈)的栈不是固定的,他可以按需增大和缩小(典型大小2KB, 限制可达1GB)
runtime
- runtime.Gosched() 让出CPU时间片,重新等待调度。
- runtime.Goexit() 退出当前协程
- runtime.GOMAXPROCS(int) (最大256)
调度器使用GOMAXPROCS参数来确定需要使用多少个OS线程来同时执行Go代码。
默认值是机器上的CPU核心数。
例如在一个8核心的机器上,调度器会把Go代码同时调度到8个OS线程上(GOMAXPROCS是m:n调度中的n)。 - goroutine与os线程是M:N的关系
Channel
- CSP并发模型(Communicating Sequential Processes),提倡通过通信共享内存而不是通过共享内存而实现通信。
- 带缓存的channel和不带缓存的channel<阻塞通道>
- 创建不带缓存的 ch := make(chan interface{})
- 创建带缓存的 ch := make(chan interface{}, num int)
- 只读的 ch := make(<-chan interface{})
- 只写的 ch := make(chan<- interface{})
- 发送 ch <- 10
- 接收 x := <- ch // 从ch中接收值并赋值给变量x
<- ch // 从ch中接收值,忽略结果 - 关闭 close(ch) // 管道不存取时一定要关闭
- 长度 len(ch) // 求缓冲通道中元素的数量
- 容量 cap(ch) // 求缓冲通道的容量
两者的区别
不带缓冲的通道,发送和接收操作都会阻塞当前协程
带缓冲的通道,进一次长度 +1,出一次长度 -1,如果长度等于缓冲长度时,再进就会阻塞
管道会出现panic地场景: 1. close以后的管道,再写入。 2. 重复close
只读的chan不能close, close以后还可以读取数据
for-range管道,遍历完后,如果chan是关闭的,遍历完数据,正常退出。
for-range管道,遍历完后,如果chan不是关闭的,遍历完数据,程序会行等待,直到出现死锁。
eg1:采用无缓冲通道进行通信
package main
import "fmt"
func recv(c chan int) {
ret := <-c
fmt.Println("接收成功", ret)
}
func main() {
ch := make(chan int)
go recv(ch) // 启用goroutine从通道接收值
ch <- 10
fmt.Println("发送成功")
}
无缓冲通道上的发送操作会阻塞,直到另一个goroutine在该通道上执行接收操作,这时值才能发送成功,两个goroutine将继续执行。
相反,如果接收操作先执行,接收方的goroutine将阻塞,直到另一个goroutine在该通道上发送一个值。
使用无缓冲通道进行通信将导致发送和接收的goroutine同步化。因此,无缓冲通道也被称为同步通道。
eg2:按顺序实现输入输出
package main
import "fmt"
func main() {
ch := make(chan int)
go func() {
for i := 0; i < 5; i++ {
ch <- i
fmt.Println("write num is :", i)
<- ch //这里必须,否则实现交替输出后,main程无法退出
}
close(ch)
}()
for { //主协程只负责读取chan中的数据
if data, ok := <- ch; ok {
fmt.Println("read num is :", data)
} else {
fmt.Println("no data")
break
}
}
}
eg3:交替打印数字和字母
package main
func PrintNums(f chan int, wg *sync.WaitGroup) {
defer wg.Done()
for i := 0; i < 5; i++ {
fmt.Println("num", i)
f <- 1
<- f
}
}
func PrintChars(f chan int, wg *sync.WaitGroup) {
defer wg.Done()
for i := 0; i < 5; i++ {
fmt.Println("char", string('a' + i))
<- f
f <- 1
}
}
func main() {
runtime.GOMAXPROCS(8)
flag := make(chan int)
var wg sync.WaitGroup
wg.Add(2)
go PrintNums(flag, &wg)
go PrintChars(flag, &wg)
wg.Wait()
}
Time包的定时器功能
package main
import (
"fmt"
"time"
)
func main() {
// 1.获取ticker对象
ticker := time.NewTicker(1 * time.Second)
i := 0
// 子协程
go func() {
for {
//<-ticker.C
i++
fmt.Println(<-ticker.C)
if i == 5 {
//停止
ticker.Stop()
}
}
}()
time.Sleep(6 * time.Second)
}
GMP模型
- Goroutine:相当于OS的进程控制块(Process Control Block);它包含:函数执行的指令和参数,任务对象,线程上下文切换,字段保护,和字段的寄存器。
- M:对应物理线程。
- P:golang的协程调度器。P的数量可以通过GOMAXPROCS设置。
调度器设计策略
1.复用线程:
- work stealing机制:当本线程无可运行的Goroutine时,尝试从其他线程绑定的P队列偷取G,而不是消毁线程。
- hand off机制:当本线程因为Goroutine进行系统调用阻塞时,线程释放绑定的P,把P转移给其它空闲的线程执行。
2.抢占:在goroutine中要等待一个协程主动让CPU才执行下一个协程;在GO中,一个goroutine最多占用CPU 10ms, 防止其他goroutine被锁死。
3.利用并行:利用GOMAXPROCS设置P数量,最多有GPMAXPROCS个线程分布在多个CPU上同时执行。
4.全局G队列:当M执行work stealing从其它P的本地队列中偷不到G时,它可以从全局列队获取G.
有疑问加站长微信联系(非本文作者)