这是我参与「第三届青训营 -后端场」笔记创作活动的第3篇笔记;
defer延迟调用
defer后面跟一个函数,意为当前函数执行完了再执行defer后面跟的这个函数,例如:
package main
import (
"fmt"
)
func say(){
fmt.Print("1\t")
}
func main(){
defer say()
fmt.Print("2\t")
fmt.Print("3\t")
}
输出结果是:2 3 1
但是值得注意的是defer执行演示调用了函数,该函数里面传的参数并不受到defer后面操作的影响,例如:
func say(name string){
fmt.Print(name)
}
func main(){
name:="mike"
defer say(name)
name="张三"
}
输出结果是mike
还有就是defer的反序调用,如果函数里面有多个defer,那么最终执行顺序是先执行后面的defer函数,再执行前面的defer函数,如下:
func say(name string){
fmt.Print(name)
}
func main(){
defer say("name1\t")
defer say("name2\t")
defer say("name3\t")
}
输出结果是name3 name2 name1
go mod
go modules是最新版的依赖管理方案,在v1.11版本正式推出,有一个参数GO111MODULE表示go mod的开关:
GO111MODULE=off关闭go mod,编译时从GOPATH和vender查找依赖;GO111MODULE=on开启go mod,编译时只根据go.mod下载依赖;GO111MODULE=auth,当项目在$GOPATH$/src外面并且根目录有go.mod文件时自动开启模块支持。
示例:
首先在$GOPATH$外键一个项目,并在go文件中引入依赖:
import (log "github.com/sirupsen/logrus")
之后在此go文件处执行命令go mod init 文件名.go进行初始化,初始化之后在此文件旁边会生成一个go.mod文件;
之后执行命令go mod download:手动触发下载依赖包到本地cache(默认为$GOPATH/pkg/mod目录下)(go mod download下载根据的是go.mod里面的require 后面的内容进行下载,用于克隆别人的项目然后下载相应的依赖,因为一个新的项目go mod init初始化后生成的go.mod文件中没有require ,一个新项目需要先go get 相应依赖来进行下载)
go mod graph: 打印项目的模块依赖结构;
go mod tidy:添加需要的依赖,删除不需要的依赖;
并发编程
并发指的是多线程程序在一个核的cpu上运行,并行指的是并发指的是多线程程序在多个核的cpu上运行;
- 线程:可以理解为用户态的轻量级线程,栈是MB级别;
- 协程:是内核态的,一个线程可以跑多个协程,栈是kb级别;
golang调用子协程示例:
package main
import (
"fmt"
"time"
)
func say(age int){
fmt.Println(age)
}
func main(){
for i:=0;i<5;i++{
go say(i)//go后面加一个函数表示使用一个子协程执行这个函数
}
time.Sleep(time.Second) //Sleep阻塞当前go线程一段时间,以免还未打印出结果程序已结束
}
输出结果(并未按照顺序执行):
4
2
3
1
0
并发安全
这样的话使用go 函数就会引起一系列的并发安全问题,如下:
func say(num *int)(int){
for i:=0;i<100;i++{
*num++;
}
return *num
}
func main(){
a:=0
for i:=0;i<10;i++{
go say(&a)
}
time.Sleep(time.Second)
defer fmt.Print(a)
}
多次执行,发现结果并不一定都是我们期待的1000:
我们可以通过加锁的方式解决这个问题:
var (
lock sync.Mutex//创建锁对象
)
func say(num *int)(int){
for i:=0;i<100;i++{
lock.Lock()//加锁
*num++;
lock.Unlock()//释放锁
}
return *num
}
func main(){
a:=0
for i:=0;i<10;i++{
go say(&a)
}
time.Sleep(time.Second)
defer fmt.Print(a)
}
go web
分层结构
第一个Gin服务
Gin是一个高性能开源的go web框架,我们首先导入Gin依赖:
go get github.com/gin-gonic/gin v1.3.0然后写出的第一个运行程序:
package main
import(
"github.com/gin-gonic/gin"
)
func main(){
r :=gin.Default()// 创建一个默认的路由引擎
//GET:请求方式;/ping:请求的路径
//当客户端以GET方法请求/ping路径时,会执行后面的匿名函数
r.GET("/ping",func(c *gin.Context){
// c.JSON:返回JSON格式的数据
c.JSON(200,gin.H{
"message":"pong",
})
})
r.Run()// 启动HTTP服务,默认在localhost:8080启动服务
}
之后执行命令go mod tidy将需要的依赖进行下载,不需要的依赖进行删除,浏览器访问http://localhost:8080/ping如下:
类似于Rest风格携带参数
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
func main(){
router:=gin.Default()
//1.此处只能接受带name参数的/user访问,也就是说必须携带参数name
//2.如果想要既可以响应“/user”也可以相应“/user/xxx”的话可以将“:name”改为“*name”,与上面不同的是:
//此时如果是“/user/xxx”请求name参数就是"/xxx"而不是“xxx”
//此时如果是“/user”请求name参数就是“/”
router.GET("user/:name",func (c *gin.Context) {//这里参数是name,类似于Rest风格
//gin有自带的Context即gin.Context,其中包含了
//web端发送来的httpRequest,url等众多参数
var name string
name=c.Param("name")//取出参数name
c.String(http.StatusOK, "Hello %s", name)
//返回一个string字符串,其中http.StatusOK相当于响应码200,这里也可以写200
// c.JSON(200,gin.H{
// "message":name,
// })
})
router.Run(":80")
}
访问如下: