Go并发编程与包管理 | 青训营笔记

54 阅读3分钟

这是我参与「第五届青训营 」笔记创作活动的第1天;

(实际是开始听课的第二天,第一天误以为要把8个小时+都看完,还觉得没完成任务所以没有打卡...)

Anyway, the show is on.

前言

「青训营笔记」将作为我在营期间的记录、思考与总结,从「课堂笔记」、「Code Review」、「拓展提高」三个方面展开,一是规格化一天的收获,二是督促自己须在如上三个方面有所收获。

  • 课堂笔记:将听课时的随堂笔记以摘要的形式分享出来,巩固课堂所学;

  • Code Review:取字面意思,会通过在课后复现一遍课堂代码的方式做到对相关知识点的真正掌握,并将其中所思所想整理一二,记录如下;

  • 拓展提高:在时间允许的情况下会做一些延伸阅读或代码实验,以期触类旁通、学以致用,并将收获记录于此;

课堂笔记

Go语言基础与简单项目实现

  • Go语言的随机数获取与种子设定:
import (
    "math/rand"
    "time"
)
rand.Seed(time.Now().UnixNano()) // 取当前时间戳为随机种子
randNum := rand.Intn(maxNum) // 取随机整数
  • 输入流读取与字符串裁剪:
reader := bufio.NewReader(os.Stdin)
input, err := reader.ReadString('\n')
input = strings.Trim(input, "\r\n")
  • 关于DevTool的使用,恰与我前端开发经验重合,Network面板可读取浏览器发送的请求及其响应与状态;

  • 工具网站大集合:

  • JSON序列化与反序列化——用结构体装下:

// 序列化
buf, err := json.Marshal(request)
if err != nil {
    log.Fatal(err) // 错误处理
}

// 反序列化
err = json.Unmarshal(bodyText, &dictResponse) // 别丢,若写入

并发编程

  • 协程是Go语言的执行单元,实质为用户级线程,可类比Java的Thread;
  • 通道,chan,区分有无缓冲,可在初始化时指定。Go语言用通道实现共享内存而不是相反,可以提高异步程序同步效率;
  • 关键字defer,延迟执行,逆序回收:
go  func(){
    defer close(chan1) // 并发相关一定记得defer关闭,避免内存泄漏?
    // defer conn.Close()    
} ()
  • sync包提供加锁操作,使用waitGroup实现协程同步:
import (
    "sync"
)

var (
    lock sync.Mutex
    wg   sync.WaitGroup
)

lock.Lock() // 上锁
lock.Unlock() // 关锁

wg.Add(num) // 计数器
wg.Wait()
defer wg.Done()

Code Review

  • 占位变量
for _, u := range users { // Tips:占位变量_,接收的值在之后不被使用
    if u.name == name {
        return &u, nil
    }
}
  • 对第三节课并发编程最后一个示例的改进:
var (
    x    int64
    lock sync.Mutex
    wg   sync.WaitGroup // 增加WaitGroup变量,使用协程等待方式代替Sleep实现协程同步
)
// ...
func addWithoutLock() {
    defer wg.Done()    // 计数器-1
    // ...
}

func addWithLock() {
    defer wg.Done() // 计数器-1
    // ...
}

func Add() {
    x = 0
    for i := 0; i < 5; i++ {
        wg.Add(1)    // 计数器+1
        go addWithoutLock()
    }
    wg.Wait()    // 阻塞等待
    fmt.Println("WithoutLock:", x)
    x = 0
    for i := 0; i < 5; i++ {
        wg.Add(1)    // 计数器+1
        go addWithLock()
    }
    wg.Wait()    // 阻塞等待
    fmt.Println("WithLock", x)
}

拓展提高

  • Go语言中声明变量与赋值、new和make的区别

声明变量与赋值:变量值为变量值本身;

new:返回声明的引用类型变量的指针;

type MyStruct struct {
    name string
    age  int
}

func main() {
    a := new(int)
    *a = 3
    fmt.Printf("%T,%p,%p,%v\n", a, &a, a, a) // *int,0xc00000a028,0xc00000e0a8,0xc00000e0a8

    b := new(MyStruct)
    fmt.Printf("%T,%p,%p,%v\n", b, &b, b, b.age) // *main.MyStruct,0xc00000a038,0xc000008078,0

    c := new(MyStruct)
    c = &MyStruct{"zhaoshaofeng", 21}
    fmt.Printf("%T,%p,%p,%v\n", c, &c, c, c.age) // *main.MyStruct,0xc00000a040,0xc0000080a8,21
}

make:只能用于slice、map、channel,返回初始化的引用类型变量;

a := new([]int)
b := new(map[string]int)
c := new(chan int)