#后端go语言课程笔记|青训营笔记
这是我参与「第五届青训营 」伴学笔记创作活动的第 2 天,通过学习记录并总结部分知识点,不足之处请指正。 本章知识点有以下方面:
并发VS并行
什么是并发,什么是并行?
并发是不同的代码块交替执行,也就是交替可以做不同的事情。 并行是不同的代码块同时执行,也就是同时可以做不同的事情。
package main
import (
"fmt"
"runtime"
"sync"
)
func main() {
runtime.GOMAXPROCS(1)
app := sync.WaitGroup{}
app.Add(20)
for i := 0; i < 10; i++ {
go func() {
fmt.Println("go ---> 1 i: ", i)
app.Done()
}()
}
for i := 0; i < 10; i++ {
go func(i int) {
fmt.Println("go ---> 2 i: ", i)
app.Done()
}(i)
}
app.Wait()
注意:并发不等于并行
go中的所有的goroutines都是只在一个线程里面跑的。即,以上两个代码不是并行,确是并发的。
信道
信道(Channel)可以被认为是协程之间通信的管道。与水流从管道的一端流向另一端一样,数据可以从信道的一端发送并在另一端接收。
信道使用前都需要声明,每个信道都有一个与之关联的类型。此类型是允许信道传输的数据类型,除此类型外不能通过信道传输其他类型。
提倡通过信道共享内存而不是通过共享内存而实现通信
并发安全 lock
如果没有锁,在我们的项目中,可能会存在多个goroutine同时操作一个资源(临界区),这种情况会发生数据竞态。
var x int64
var wg sync.WaitGroup
func add() {
for i :=0;i<1000;i++{
x = x + 1
}
wg.Done()
}
func main() {
wg.Add(2)
go add()
go add()
wg.Wait()
fmt.Println(x)
}
如上,这两个goroutine在访问和修改x变量的时候就会出现数据竞争,最后的结果与预期结果不一致。
sync.WaitGroup
Go语言中可以使用sync.WaitGroup来实现并发任务的同步。sync.WaitGroup有以下几个方法:
| 方法名 | 功能 |
|---|---|
| (wg * WaitGroup) Add(delta int) | 计数器 + delta |
| (wg *WaitGroup) Done() | 计数器-1 |
| (wg *WaitGroup) Wait() | 阻塞直到计数器变为0 |
sync.WaitGroup内部维护着一个计数器,计数器的值可以增加和减少
channel
Channel是go中的一个核心类型,类似于一个管道,通过它并发核心单元就可以发送或者接收数据进行通讯(communication)。
创建通道是引用类型,通道类型的空值是nil。声明的通道需要使用make函数初始化之后才能使用。创建channel的格式如下:
make (chan 元素类型,[缓冲大小])
其中,channel的缓冲大小是可选的。
channel操作有:发送(send)、接收(receive)和关闭(close)三种操作。其中,发送和接收都使用<-符号。关闭是调用内置的close函数来关闭通道。
注意:
- 向一个nil channel发送消息数据,则会一直阻塞;
- channel关闭后不可以继续向channel发送消息,但可以继续从channel接收消息;
- 通道是可以被垃圾回收机制回收的,关闭通道不是必须的。