这是我参与[第五届青训营]伴学笔记创作活动的第2天
一.本堂课重点内容
- Go语言进阶
二.详细知识点介绍
Go语言进阶
- 并发 VS 并行
并发指多线程程序在一个核的cpu上运行
并行指多线程程序在多个核的cpu上运行
- Goroutine
Goroutine也叫 Go 程,是由 Go 运行时管理的轻量级线程
线程 : 用户态,轻量级线程,栈MB级别
协程 : 内核态,线程跑多个协程,栈KB级别
可以用go加方法名启动一个新线程并执行
go f(x, y, z)
f(x, y, z)
f, x, y 和 z 的求值发生在当前的 Go 程中,而 f 的执行发生在新的 Go 程中
Go 程在相同的地址空间中运行,因此在访问共享的内存时必须进行同步。
- CSP(Communicating Sequential Processes)
CSP中文名称是通信顺序进程,Go语言中提倡通过通信共享内存
- Channel
Channel在Go的官方文档中解释为信道,它分为无缓冲通道和有缓冲通道两种
信道是带有类型的管道,你可以通过它用信道操作符 <- 来发送或者接收值。
ch <- v // 将 v 发送至信道 ch。
v := <-ch // 从 ch 接收值并赋予 v。
(“箭头”就是数据流的方向。)
和映射与切片一样,信道在使用前必须创建(无缓冲通道创建方法):
ch := make(chan int)
默认情况下,发送和接收操作在另一端准备好之前都会阻塞。这使得 Go 程可以在没有显式的锁或竞态变量的情况下进行同步。
以下示例对切片中的数进行求和,将任务分配给两个 Go 程。一旦两个 Go 程完成了它们的计算,它就能算出最终的结果。
package main
import "fmt"
func sum(s []int, c chan int) {
sum := 0
for _, v := range s {
sum += v
}
c <- sum // 将和送入 c
}
func main() {
s := []int{7, 2, 8, -9, 4, 0}
c := make(chan int)
go sum(s[:len(s)/2], c)
go sum(s[len(s)/2:], c)
x, y := <-c, <-c // 从 c 中接收
fmt.Println(x, y, x+y)
}
-5 17 12
信道可以是带缓冲的。将缓冲长度作为第二个参数提供给 make 来初始化一个带缓冲的信道:
ch := make(chan int, 100)
仅当信道的缓冲区填满后,向其发送数据时才会阻塞。当缓冲区为空时,接受方会阻塞。
- 并发安全Lock
我们已经看到信道非常适合在各个 Go 程间进行通信。
但是如果我们并不需要通信呢?比如说,若我们只是想保证每次只有一个 Go 程能够访问一个共享的变量,从而避免冲突?
这里涉及的概念叫做 互斥(mutualexclusion),我们通常使用 互斥锁(Mutex) 这一数据结构来提供这种机制。
Go 标准库中提供了 sync.Mutex 互斥锁类型及其两个方法:
LockUnlock
我们可以通过在代码前调用 Lock 方法,在代码后调用 Unlock 方法来保证一段代码的互斥执行。参见 Inc 方法。
我们也可以用 defer 语句来保证互斥锁一定会被解锁。参见 Value 方法。
package main
import (
"fmt"
"sync"
"time"
)
// SafeCounter 的并发使用是安全的。
type SafeCounter struct {
v map[string]int
mux sync.Mutex
}
// Inc 增加给定 key 的计数器的值。
func (c *SafeCounter) Inc(key string) {
c.mux.Lock()
// Lock 之后同一时刻只有一个 goroutine 能访问 c.v
c.v[key]++
c.mux.Unlock()
}
// Value 返回给定 key 的计数器的当前值。
func (c *SafeCounter) Value(key string) int {
c.mux.Lock()
// Lock 之后同一时刻只有一个 goroutine 能访问 c.v
defer c.mux.Unlock()
return c.v[key]
}
func main() {
c := SafeCounter{v: make(map[string]int)}
for i := 0; i < 1000; i++ {
go c.Inc("somekey")
}
time.Sleep(time.Second)
fmt.Println(c.Value("somekey"))
}
- WaitGroup
WaitGroup底层是一个计数器,通过实现Add方法向计数器中添加指定值。通过实现Done方法来实现计数器的-1操作。当协程开始并且达到计数器指定数目时,程序便会阻塞,也可通过Wait方法使程序进入无限额的等待,当计数器值减为0时,阻塞的程序便会释放,然后继续执行下去。
三.课后个人总结
在学习Go语言进阶课程得过程中,有许多东西需要我们自己反复去揣摩,更多的是理解它怎么用,为什么这么用,除了课程所教授的,这些东西还有什么别的特点,每次课后,我都会去查阅Golang的官方文档,看官方对Go语言基础或进阶给出的解释又是如何,学习就是不断攀登的过程,只有不懈努力,才会获得成功。
四.引用
- 字节內部课 - Go语言入门
- Golang官方文档