这是我参与「第五届青训营 」伴学笔记创作活动的第 2 天
协程
协程:用户态,轻量级线程,栈MB级别 线程:内核态,线程跑多个协程,栈KB级别
优势
1.消耗小:每个2kb内存,可以轻松创建大量的goroutine 2.启动时间快于线程 3.原生支持通过channel进行通信,go推荐使用通信来并发而不是内存共享,不用操心锁和同步
启动协程
func print(sum int) {
fmt.Println(sum)
}
func main() {
for i := 1; i <= 10; i++ {
go print(i)
}
time.Sleep(time.Second)
}
使用go关键字进行协程的启动
CSP
通过通信的方式来共享内存
Channel(缓冲)
无缓冲通道:make(chan 类型) 例:make(chan int) 有缓冲通道:make(chan 类型,大小) 例:make(chan int,2)
func print(sum int) {
fmt.Printf("%v\n", sum)
}
func main() {
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 {
print(i)
}
}
通过使用Channel保证了并行时执行的顺序
如果不使用缓冲区,那么dest会阻塞到数字被拿走再继续执行,如果有缓存,那么直到缓冲区满才会被阻塞,再某些情况下,缓冲区的大小也是值得商榷的事情
并发安全Lock
var (
x int64
lock sync.Mutex
)
func addlock() {
for i := 0; i < 2000; i++ {
lock.Lock()
x += 1
lock.Unlock()
}
}
func add() {
for i := 0; i < 2000; i++ {
x += 1
}
}
func main() {
for i := 0; i < 5; i++ {
go add()
}
time.Sleep(time.Second)
fmt.Println(x)
x = 0
for i := 0; i < 5; i++ {
go addlock()
}
time.Sleep(time.Second)
fmt.Println(x)
}
如果使用共享内存的方法,那就有可能出现多个协程对一块内存区域进行修改,而导致修改丢失的问题,所以我们得使用Muter互斥锁进行解决
WaitGroup
GO语言还可以使用WaitGroup解决多个并发任务的同步
ADD(delta int) 计数器加delta Done() 计数器-1 Wait() 阻塞到计数器为0
func main() {
var wg sync.WaitGroup
wg.Add(5)
for i := 0; i < 5; i++ {
go func(j int) {
defer wg.Done()
fmt.Println(j)
}(i)
}
wg.Wait()
}
##总结 这次的课程学习到了Go语言的并发操作,之后还需要更多的应用对相关的操作进行熟悉