工程实践 | 青训营笔记

100 阅读3分钟

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

工程实践

1.并发与并行

并发是指多线程程序在单核cpu上运行,通过 时间片切换 实现同时运行状态,宏观上并发,微观上交替

并行是指多线程程序在多核cpu上运行,go语言实现了并发性能提高的调度模型,go语言为并发而生。

2.Goroutine(协程)

线程:内核态、线程可以跑多个协程,栈MB级别

协程:用户态、轻量级线程、栈KB级别

func hello(i int) {
    println("hello goroutine:" + fmt.Sprint(i))
}
func HelloGoRoutine(){
    for i := 0; i < 5; i++ {
        go func(j int){ //只需要在函数前加上go 即可创建协程,真是干净又卫生
            hello(j)
        }(i)
    }
    time.Sleep(time.Second) //是子协程未完成之前,主协程不退出
}

协程的方式 对for循环 输出的结果是 乱序 的,因为是 HelloGoRoutine 是通过并行的方式输出,但是 速度快

3.CSP(Communicating Sequential Processes)

在go语言中提倡通过通信实现共享内存而不是通过共享内存实现通信

通过通信实现共享内存是通过通道的形式对协程进行连接,在数据传输时遵行先入先出,能保证收发数据有序性

通过共享内存实现通信必须通过互斥量对内存进行加锁,协程需要获取临界区的权限,不同的协程之间会容易产生数据静态的问题,可能会影响程序性能。

4.Channel(通道)

chan是引用类型,需要make关键字进行创建

make(chan 元素类型, [缓冲大小])//创建无缓冲通道
make(chan int)//创建无缓冲通道
make(chan int, 2)//创建有缓冲通道

无缓冲通道发送和接收同步进行,也被称为同步通道,解决同步问题就是使用有缓冲通道

样例

  1. A子协程发送0-9数字
  2. B子协程计算输入数字的平方
  3. 主协程输出
func CalSquare() {
    src := make(chan int)// 创建src无缓冲通道
    dest := make(chan int, 3)// 创建dest有缓冲通道
    go func() { //A协程
        defer close(src) // defer延迟资源关闭
        for i := 1; i < 10; i++ {
            src  <- i //把数字发送到src通道
        }
    }()
    go func() { //B协程
        defer close(dest)
        for i := range src { // 这里通过src通道实现了AB协程的通信
            dest  <- i * i
        }
    }()
    for i := range dest {
        println(i)
    }
}

可以保证结果的顺序性,即并发安全

5.并发安全Lock

//实现1-2000相加
var(
    x int64
    lock sync.Mutex
)
//未加锁
func addWithoutLock(){
    for i := 0; i <2000; i++ {
        x += 1
    } 
}
//通过加锁解锁并发安全问题
func addWithLock(){
    for i := 0; i <2000; i++ {
        lock.Lock()
        x += 1
        lock.Unlock()
    } 
}
//在线程中创建多个协程并发的运行
func add() {
    x = 0
    for i := 0; i < 5; i++ {
        go addWithoutLock()
    }
    println(x)// 随机未知结果 不安全
    
    x = 0
    for i := 0; i < 5; i++ {
        go addWithLock()
    }
    println(x)//10000 结果正确
}
​

6.WaitGroup

对比标题2,time.Sleep实现并发同步时不够优雅,WaitGroup可以优雅的实现并发任务的同步,提供了三个方法

func hello(i int) {
    println("hello goroutine:" + fmt.Sprint(i))
}
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()// 方法三 阻塞直到计数为0
}