字节青训营笔记----day2(1)

92 阅读2分钟

课程目录

1. 语言进阶(并发编程)

2. 依赖管理(go module)

  1. 测试(单元测试)

  2. 项目实战

1. 语言进阶

并发 vs 并行

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

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

goroutine:轻量级线程,可以认为几乎不占用内存(KB 级别),可以轻松开启很多 goroutine(golang 在语言层面实现了并发)

代码实例

func HelloGoRoutine() {
    for i := 0; i < 5; i++ {
        go func(j int) {
            fmt.Println(j)
        }(i)
    }
    
    time.Sleep(1 * time.Second)
}

golang 提倡通过通信共享内存,而不是共享内存来通信(遵循 CSP 通信模型,Communicating Sequential Processes)

image.png

协程间通信可以通过 channel 实现,channel 包含无缓冲通道和有缓冲通道两种,无缓冲通道又称为同步通道,有缓冲通道也称为异步通道

代码实例

A 子协程发送 0~9 数字

B 子协程计算输入数字的平方

主协程输出最后的平方数

func CalSquare() {
    src := make(chan int)
    // 在消费端使用有缓冲通道,防止消费速度影响生产者的生产速度
    dest := make(chan int, 3)

    // A
    go func() {
        defer close(src)
        
        for i := 0; i < 10; i++ {
            src <- i
        }
    }()
    
    // B
    go func() {
        defer close()
        
        for i := range src {
            dest <- i * i
        }
    }()
    
    // 主 goroutine
    for i := range dest {
        fmt.Println(i)
    }
}

并发安全 Lock

代码实例

var (
    x int64
    lock sync.Mutex // sync 包中的 Mutex 是并发安全的
)

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

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

func Add() {
    x = 0
    for i := 0; i < 5; i++ {
        go addWithoutLock()
    }
    
    time.Sleep(time.Second)
    fmt.Println("WithoutLock:", x)
    
    x = 0
    for i := 0; i < 5; i++ {
        go addWithLock()
    }
    
    time.Sleep(time.Second)
    fmt.Println("WithLock:", x)
}

image.png

WaitGroup

wg.Add(): 开启一个协程,计数器 +1

wg.Done(): 一个协程结束,计数器 -1

wg.Wait(): 主 goroutine 阻塞,直到计数器值为 0(等待子 goroutine 结束)

代码实例

func ManyGoWait() {
    var wg sync.WaitGroup
    
    wg.Add(5)
    for i := 0; i < 5; i++ {
        // 这种匿名函数可以防止最终打印的都是同一个地址中的变量值
        go func(j int) {
            defer wg.Done()
            hello(j)
        }(i)
    }
    
    wg.Wait()
}

2. 依赖管理

发展历程:GOPATH -> Go vendor -> Go Module

GOPATH 弊端:无法实现 package 的多版本控制

Go vendor:所有依赖包副本形式放在 vendor 文件夹中,无法控制依赖的版本,更新项目有可能出现依赖冲突,导致编译出错

Go Module:通过 go.mod 文件管理依赖包版本,通过 go getgo mod 指令工具管理依赖包

  • go mod init example/xyz 初始化,新建 go.mod 文件
  • go mod download 下载模块到本地缓存
  • go mod tidy 增加需要的依赖,清理不必要的依赖
  • go get github.com/xyz/abc@version 获取指定版本的

依赖管理三要素

1、配置文件,描述依赖 (go.mod)

2、中心仓库管理依赖库(Proxy)

3、本地工具(go get/mod)

  • go.mod

image.png

  • version

语义化版本:${MAJOR}.${MINOR}.${PATCH}

v1.3.0

v2.3.0

基于 commit 的伪版本:vx.0.0-yyyymmddhhmmss-abcdefgh1234

v0.0.0-20220401081311-c38fb59326b7c

v1.0.0-202001130134442-10cb98267c6c

  • indirect

image.png

  • incompatible

image.png

  • 依赖图

image.png

在依赖版本选择时,选择最低兼容版本,即使有 C 1.5版本,最终也会选择 C 1.4版本进行编译。

  • 依赖分发

直接将依赖分发给第三方代码托管平台存在的问题

  1. 无法保证构建稳定性(软件作者可以直接在代码托管平台对软件版本进行增加、修改或删除)
  2. 无法保证依赖的可用性(软件作者可以对软件仓库进行删除)
  3. 增加了第三方的压力(代码托管平台的负载问题)

使用 PROXY

image.png

Golang 的 PROXY 是一个软件站点,它会缓存源站中的软件内容,缓存里的软件版本不会改变,可以保证可靠稳定的依赖分发.

  • 变量 GOPROXY

GORPOXY="proxy1.cn, proxy2.cn, direct" 服务站点 URL 列表,“direct”表示源站

image.png

可以使用 go env | grep GOPROXY 查看 GOPROXY 的值

image.png

源码下载到本地的 /Users/apple/Public/青训营/day2