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

前言

根据前文的 Go 言语基础语法,本文首要介绍 Go 言语进阶编程中的并发编程,从并发编程的视角介绍 Go 言语高性能的本质。

重点内容

  • 协程 Goroutine
  • 通道 Channel
  • 锁 Lock
  • 线程同步 WaitGroup

常识点介绍

Go 言语能够充分发挥多核优势

协程 Goroutine

协程运行在线程之上,协程并没有增加线程数量,只是在线程的基础之上通过分时复用的方式运行多个协程,而且协程的切换在用户态完结,切换的价值比线程从用户态到内核态的价值小很多。

创立一个协程十分简单,就是在一个使命函数前面增加一个 go 关键字:

go func()

下面一个示例:

// 打印 hello goroutine : 0 - 4
func hello(i int) {
    println("hello goroutine : " + fmt.Sprint(i))
}
​
func HelloGoRoutine() {
    for i := 0; i < 5; i++ {
        go func(j int) {
            hello(j)
        }(i)
    }
    time.Sleep(time.Second)
}

成果并不是次序打印出来,可见不同协程并发履行,次序不同。

通道 Channel

Go 提供一种称为通道的机制,用于在 Goroutine 之间共享数据,确保协程在履行并发活动时同步交流数据,在同一时刻只要一个协程能够拜访通道数据项。

通道包括:

  • 无缓冲通道:同步通讯

    unBuf := make(chan int)
    
  • 有缓冲通道:异步通讯

    buf := make(chan int, 10)
    

运用协程和通道完结一个生产者顾客模型:a 子协程发送数字0-9,b 子协程核算数字的平方,主协程输出最终的平方数

func CalSquare() {
    src := make(chan int)
    dest := make(chan int, 3)
    go func() {
        defer close(src)
        for i := 0; i < 10; i++ {
            src <- i
        }
    }()
    go func() {
        defer close(dest)
        for i := range src {
            dest <- i * i
        }
    }()
    for i := range dest {
        //杂乱操作
        println(i)
    }
}

运用 for range 从通道中读取数据,运用 <- 向通道内写数据。

锁 Lock

在有两个或更多 goroutine 的程序中,每一个 goroutine 内的语句也是按照既定的次序去履行的,可是一般情况下没法去知道别离坐落两个 goroutine 的事件 x 和 y 的履行次序,x 是在 y 之前还是之后还是同时产生是没法判断的。当没有办法自信地确认一个事件是在另一个事件的前面或许后面产生的话,就说明 x 和 y 这两个事件是并发的。

并发编程很简单呈现竞赛,一般通过加锁的方式完结同步

var (
    x  int64
    lock sync.Mutex
)
func addWithLock() {
    for i := 0; i < 2000; i++ {
        lock.Lock()
        x += 1
        lock.Unlock()
    }
}
func addWithoutLock() {
    for i := 0; i < 2000; i++ {
        x += 1
    }
}
​
func Add() {
    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)
}

线程同步 WaitGroup

Go言语中除了能够运用通道(channel)和互斥锁进行两个并发程序间的同步外,还能够运用等候组进行多个使命的同步,等候组能够确保在并发环境中完结指定数量的使命。

WaitGroup 一共有三个办法

  • Add 办法用于设置 WaitGroup 的计数值,能够理解为子使命的数量
  • Done 办法用于将 WaitGroup 的计数值减一,能够理解为完结一个子使命
  • Wait 办法用于堵塞调用者,直到 WaitGroup 的计数值为0,即一切子使命都完结
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()
}

总结

本文首要介绍了 Go 言语并发编程的相关常识,为了应对高拜访量,现代体系大都采用并发编程,Go 言语又是为并发编程所生,因而这部分常识需要熟练掌握,应用于后续大作业。