工程实践|Go语言基础|青训营笔记

68 阅读1分钟

##这是我参与[第五届青训营]笔记创作的第二天

1、语言进阶

1、并发 vs 并行

并发:多线程程序在一个核的cpu上运行

并发:多线程程序在多个核的cpu上运行

Go可以充分发挥多核优势,高效运行

2、协程

协程:用户态,轻量级线程,栈kb级别

例句:快速打印:

package main
​
import (
    "fmt"
    "time"
)
​
func hello(i int) {
    println("hello goroutine : " + fmt.Sprint(i))
}
func main() {
    for i := 0; i < 5; i++ {
        go func(j int) { //通过go来开启协程
            hello(j)
        }(i)
    }
    time.Sleep(time.Second)
​
}
1、协程之间的通信

通过通信共享内存

提倡通过通信共享内存而不是通过共享内存而实现通信

2、Channel

make(chan元素类型,【缓冲大小】)

  • 无缓冲通道 make(chan int)
  • 有缓冲通道 make(chan int,2)
package main
​
import "fmt"func main() {
    src := make(chan int)
    dest := make(chan int, 3)
    go func() {
        defer close(src) //推迟关闭src
        for i := 0; i < 10; i++ {
            src <- i //将i发送到src
        }
    }()
    go func() {
        defer close(dest)    //推迟关闭dest
        for i := range src { //遍历src赋值给i
            dest <- i * i //将i*i 发送到dest
        }
    }()
    for i := range dest {
        fmt.Println(i)
    }
}
3、并发安全 Lock

这里运用到了一个sync包,使用Mutex类型锁实现

Mutex 是最简单的一种锁类型,同时也比较暴力,当一个 goroutine 获得了 Mutex 后,其他 goroutine 就只能乖乖等到这个 goroutine 释放该 Mutex。

package main
​
import (
    "sync"
    "time"
)
​
var (
    x    int64
    lock sync.Mutex
)
​

以下写两种,一种写了锁,一种不写锁

写了锁,通过locl.lock()获得锁

func addWithLock() {
    for i := 0; i < 2000; i++ {
        lock.Lock() //获得锁
        x = x + 1
        lock.Unlock()
    }
}

不写锁

func addWithOutLock() {
    for i := 0; i < 2000; i++ {
        x = x + 1
    }
}

开启锁协程

func main() {
    x = 0
    for i := 0; i < 5; i++ {
        go addWithOutLock() // 开启协程
    }
}

开启没锁协程

for i := 0; i < 5; i++ {
        go addWithLock()
    }

同时运用time.Sleep(time.second)阻塞一个协程的执行直到d时间结束。

time.Sleep(time.Second)

最后组合起来:

package main
​
import (
    "sync"
    "time"
)
​
var (
    x    int64
    lock sync.Mutex
)
​
func addWithLock() {
    for i := 0; i < 2000; i++ {
        lock.Lock() //获得锁
        x = x + 1
        lock.Unlock()
    }
}
func addWithOutLock() {
    for i := 0; i < 2000; i++ {
        x = x + 1
    }
}
​
func main() {
    x = 0
    for i := 0; i < 5; i++ {
        go addWithOutLock()
    }
    time.Sleep(time.Second)
    println("WithoutLock", x)
    x = 0
    for i := 0; i < 5; i++ {
        go addWithLock()
    }
    time.Sleep(time.Second)
    println("WithLock", x)
}
4、WaitGroup

属于Syns包

实现并发任务的同步

拥有三个方法:

  1. Add() 计数器+delta
  2. Done() 计数器-1
  3. Wait() 阻塞直到计数器0

计数器: 开启协程+1;执行结束-1;主协程阻塞直到计数器为0。

可以通过以下time.Sleep例子来理解WaitGroup

func hello(i int) {
    println("hello" + fmt.Sprint(i))
}
​
func main() {
    for i := 0; i < 5; i++ {
        go func(j int) {
            hello(j)
        }(i)
    }
    time.Sleep(time.Second) // 运用time.Sleep(time.second)阻塞
}

现在使用WaitGroup

defer 语句用于延迟函数的调用,每次 defer 都会把一个函数压入栈中,函数返回前再把延迟的函数取出并执行。 Golang 中的 defer 可以帮助我们处理容易忽略的问题,如资源释放、连接关闭等

func hello(i int) {
    fmt.Println("hello goroutine :" + fmt.Sprint(i))
}
​
func main() {
    var wg sync.WaitGroup // 声明一个等待组
    wg.Add(5) //计数器+5
    for i := 0; i < 5; i++ {
        go func(i int) {
            defer wg.Done()//计数器减-1
            hello(i)
        }(i) //调用for循环的i实参
    }
    wg.Wait() // 阻塞,直到计数器为零
}

2、依赖管理

1、背景

  • 工程项目不可能基于标准库0~1编码搭建
  • 管理依赖库

2、管理演进

  • GOPATH
  • GO Vendor
  • Go Module
1、GOPATH

环境变量:

  • bin: 项目编译的二进制文件
  • pkg:项目编译的中间产物。加速编译
  • src:项目源码

项目代码直接以来src下的代码

go get 下载最新版本的包到src目录下

2、Go Vendor

项目目录下增加vendor文件

3、Go Module

通过go.mod文件管理以来包版本

通过go get/go mod指令工具管理依赖包

:定义版本规则和管理项目的依赖关系

依赖管理三要素
  1. 配置文件,描述依赖 go.mod
  2. 中心仓库管理依赖库 Proxy
  3. 本地工具 go get/mod
依赖配置-go.mod
依赖配置-version

语义化版本

${MAJOR} ${MINOR} ${PATCH}

V1.3.0

V2.3.0

基于commit 伪版本号

依赖配置-indirect

A->B->C

  • A->B直接依赖
  • A->C间接依赖

对于没有直接依赖的依赖模块会用indirect标识

依赖配置-incompatible
  • 主版本2+模块会在模块路径增加/vN后缀
  • 对于没有go.mod文件并且主版本2+的依赖,会+incompatible