目录
go的特点
- 高性能,高并发
- 语法简单,学习曲线平缓
- 丰富的标准库
- 完善的工具链
- 静态链接
- 快速编译
- 跨平台
- 垃圾回收
编程第一步“hello world”
package main
import "fmt"
func main()
{
// 终端输出hello world
fmt.Println("Hello world!")
}
相对于其他编程语言go的注意事项
1.编写源文件时,必须在非注释的第一行指明这个文件属于哪个包,如package main
2.在编写时引出我们所需要的包,如import "fmt"
其实就是引入了fmt包
3.和c语言相同,每一个可执行程序中需要拥有一个main函数
4.变量,Go 语言变量名由字母、数字、下划线组成,其中首个字符不能为数字
5.相对于c,go在语句优化上做的更好了,去掉了每句结尾时所需要的
增加端口
package main
import "net/http"
func main()
{
http.Handle("/",http.FileServer(http.Dir(".")))
http.ListenAndServe(":8888",nil)
}
基础语法
变量声明
go是属于内置类型
实现可以直接使用“+”拼接,以及使用“=”直接进行比较
变量声明
var a = "bob"
可以直接使用var和name进行变量声明 能够自动边界类型 也可以var a int = 1
进行声明
通过:=
进行声明,b :=float64(c)
常量通过const
声明,如 const a string = "conntant"
go中常量没有特定的类型,可以通过上下文进行判断
语句if else
基本和c,c++一致有两个不同点:
和c不同的是 if后面没有括号,有括号编辑器不会判定为错误,会自动删去
go中的if语句后面不能写在同一行
循环语句for
go中唯一的循环语句for
go的进阶
Goroutine
协程:用户态,轻量级线程,栈mb级别
线程:内核态,线程跑多个协程,栈kb级别
实现快速需要打开多个协程如go func(j int)
在函数前面加go
CSP
通过通信共享内存
Channel
make(chan 元素类型,[缓冲大小])
例:
-
无缓冲通道
make(chan int)
//同步通道 -
有缓冲通道
make(chan int, 2)
//容量为2的缓冲通道
src := make(chan int)
dest :=make(chan int, 3)//缓冲通道为3
go func() {
defer ckose(src)
for i := 0; i < 10; i++ {
src = i //记录小于十的数字
}
}()
go func(){
defer close(dest)
for i := range src {
dest = i * i //记录平方
}
}()
for i := range dest {
//所有复杂其他数
println(i)
}
并发安全lock
避免对共享内存进行非法操作
waitgroup
Add(delta int) //计数器+delta
Done() //计数器-1
Wait() //阻塞知道计数器为0
var wg sync.WaitGroup
wq.Add( delta: 5)
for i := 0; i < 5; i++b {
go func(j int){
defer wg.Done()
hello(j)
}(i)
}
wg.Wait()
}
依赖管理
Go 依赖管理演进
共有三个阶段
- GOPATH
- GO Vendor
- Go Module(广泛应用)
GOPATH
工作区
bin 项目编译的二进制文件
pkg 项目编译的中间产物,加速编译
src 项目原码(直接依赖)
弊端:无法实现多版本控制
Go Vendor
增加了vendor文件,通过引入依赖文件,解决多项目使用同一个package的冲突问题
弊端:
1.无法控制依赖版本
2.更新项目有可能出现冲突
Go Module
通过go.mod文件管理 实现定义版本规则和管理项目依赖关系
三要素
配置文件,描述依赖 go.mod
中心仓库管理依赖库 Proxy
本地工具 go get/mod
规范及优化
编码规范
- 代码格式
- 注释
- 命名规范
- 控制流程
- 错误和异常处理
代码格式
gofmt
能自动化 党建IDE都支持方便的配置
goimports
等于gofmt加上依赖包管理 自动增删依赖的包引用、将依赖包按字母序排列并分类
注释
- 作用
- 如何做的
- 实现的原因
- 什么情况会出错
注释应该提供代码为表达出的上下文信息
命名规范
- 简洁
- 缩写词全部大写
- 全局变量在名字中需要更多的信息
- 降低阅读代码成本
流程控制
- 避免嵌套
错误和异常处理
- 使用
errors.New
来创建匿名变量来直接标志简单错误 - 如果有格式化需求,使用
fmt.Errorf
可以用%w在关联错误 - 在错误链上获取特定种类错误,使用
errors.As
panic
- 调用函数不包括recover会造成程序崩溃
- 建议使用
error
替代panic
recover
- 只能在defer中使用
- 嵌套无法使用
- 只在当前goroutine生效
- 语句是后进先出
小结
error
尽可能提供简明的上下文信息链,方便定位问题panic
用于真正异常的情况recover
生效范围,在当前goroutine的被defer的函数中生效
性能优化
性能优化Benchmark
go test -bench=. -benchmem
测试性能
- 测试名
- 测试次数
- 花费时间
- 执行申请内存
- 每次执行申请次数
优化
预分配内存
type slice struct{
array unsafe.Pointer
len int
cap int
}
Map
- 预分配内存
- 提前分配可以减少内存拷贝和Rehash
字符串处理
使用strings.Builder 会缩短直接使用i++时间效率
在拼接时候:
func preStrBuilder(n int, str string)string{
var builder stings.builder
builder.Grow(n * len(str))
for i := 0; i < n; i++{
builder.WriteString(str)
}
return builder.String()
}
空结构体
- struct{}不占用任何空间
- 可作为占位符号 1.节省资源 2.空结构本身具备很强的语义,不需要任何值 例:实现set 可以用map代替 只需要map的键不需要值
atomic包
type atomicCounter struct{
i int 32
}
func AtomicAddOne(C
*atomicCounter){
AtomicAdd32(&c.i, i)
}
- 锁是通过系统实现,属于系统调用
- atomic是通过硬件实现,效率比锁高
- sync.Mutex 应该用来保护一段逻辑,不仅仅用于保护一个变量
- 对于非数值操作,可以使用atomic.Value,能承载一个interface{}