go语言进阶|青训营

54 阅读3分钟

1. 线程与协程

Go语言是一种轻量化语言,支持多线程在多个核上的并发运行,可以发挥多核的

优势,并行运行。

协程是一种轻量级线程,栈是kb级别,而线程是Mb级别

可以用go func(参数){

被变成协程的函数(参数)

}(引用的参数)来创建一个协程,具体实例

for j := 0; j < 5; j++ {
go func(p int) {
hello(p)
}(j)
}

这是一个用goroutine输出hello0~4的代码

package main

import (
"fmt"
"time"
)

func hello(i int) bool {
var s string = fmt.Sprintf("%d", i)
println("hello" + s)
return true
}

func hellow() {
fmt.Println("hello world")
for j := 0; j < 5; j++ {
go func(p int) {
hello(p)
}(j)
}
time.Sleep(time.Second)
}
func main() {
hellow()
}

图片22.png 可以看到0--4是乱序输出的,说明了协程是并行执行的

如果要

2. channel用于实现通过通信共享内存

图片23.png

用make(chan 通道通信的数据类型,缓冲大小(可选))

无缓冲通道这样make(chan int)

有缓冲通道这样 make(chan int,2)

两者的区别就是通道中是否设置缓冲区的区别

有缓冲通道中设有缓冲区可以暂时存放发送的数据

下面写一个生产者--消费者的例子

A协程发送当前时间

B算出当前时间与第二天的时间差

主协程再打印时间差

在函数中先创建两个channel:src和src2,然后创建第一个协程,这个协程将产生5次当前时间,每秒一次,并将它存进src中

`go func() {
//
for i := 0; i < 5; i++ {
defer close(src)
t := time.Now()
src <- t
time.Sleep(time.Second)
}

}()

`

但是实际运行时会报错:close a closed channel,原因是close语句在循环体中被运行了5次

关闭了5次,将close语句移出循环体,错误解决

`go func() {
defer close(src)
for i := 0; i < 5; i++ {
t := time.Now()
src <- t
time.Sleep(time.Second)
}

}()

`

然后依次创建协程B和主协程

`func cal() {
src := make(chan time.Time)
src2 := make(chan time.Duration, 3)
go func() {
defer close(src)
for i := 0; i < 5; i++ {
t := time.Now()
src <- t
time.Sleep(time.Second)
}

   }()
go func() {
defer close(src2)
for i := range src {
tomorrowMidnight := time.Date(i.Year(), i.Month(), i.Day()+1, 0, 0, 0, 0, i.Location())
duration := tomorrowMidnight.Sub(i)
src2 <- duration
}
}()
for i := range src2 {
fmt.Printf("%v\n", i)
} }

`

运行效果如图

图片24.png

如果将休眠语句time.sleep移出循环则是以下的运行效果

image.png

可以发现输出是按照从大到小输出的,说明协程和协程的通道通信是并发安全的

带缓冲的channel适用于协调生产者和消费者处理速度不协调的问题

3. 通过共享内存来实现协程通信

多个不同的协程同时操作一段内存,为了使进程并发安全,这时候需要设置临界区,就是加锁

图片25.png

通过下面的例子不难看出是否加锁对执行结果有一定影响,不加锁时由于多个协程使用一个内存,可能会发生多个操作同时发生的情况是某些操作被挤占而导致结果产生错误

4. Waitgroup

一个计数器,在开启协程时+1,协程结束时-1,主协程阻塞直到该计数器为0

用于协程的同步阻塞,也就是直到用了waitgroup的协程完成之前,主协程将一直阻塞

采用了waitgroup来实现协程同步的例子,和第一个例子一样

`func hello(i int) bool {
var s string = fmt.Sprintf("%d", i)
println("hello" + s)
return true
}

func hellow() {
var wg sync.WaitGroup
wg.Add(5)
fmt.Println("hello world")
for j := 0; j < 5; j++ {
go func(p int) {
defer wg.Done()
hello(p)
}(j)
}
wg.Wait()
}`