一、 协程Goroutine
通常情况下,程序在运行时只有一条主协程,按照顺序的形式来进行执行。 当我们想让程序的两个函数及多个函数进行并发 执行时,需要在函数名前加上关键字go,来为程序建立一个子协程。在子协程结束后,主协程才会结束。
通常,应用在Web服务器端来给用户提供信息。
import "fmt"
import "time"
func showMessage(msg string){
for i:=0 ; i<=6 ; i++ {
fmt.Printf("msg: %v\n", msg)
time.Sleep(time.Millisecond * 1000)
}
}
func main(){
go showMessage("hello") // go 启动了一个子协程
showMessage("world")
// defer showMessage("!!!") 当函数执行到return 才来执行defer关键字以来延迟执行
}
二、 通道channel
当程序中建立了多个协程后,各个协程之间需要进行数据通信,这就需要通道channel(管道),保证数据同步交换。
通道在使用时,也需要给它进行类型设置。什么样的类型通道就传递什么样的类型数据。
通道在使用时,也分为无缓冲和有缓冲的;无缓冲的通道在数据交换时是可以立即发生的而缓冲通道不行,有缓冲的通道是用于执行异步通信。
通道的发送和接收特性:
- 对于同一个通道而言,发送和接受数据的操作都是互斥的
- 发送和接受数据进行元素值的处理时都是不可分割的
- 发送和接受操作在完全完成之后会被阻塞
// 使用make来创建通道,chan关键字来指定通道类型
var UnChannel = make(chan int, 0) // int类型的无缓冲通道, 第二个参数默认为0
var Channel = make(chan string, 10) //string类型的缓冲通道,缓冲区设置为10个
// 发送信息到通道
// 设置一个随机数
rand.Seed(time.Now().UnixNano())
values := rand.Intn(100) // 随机一个100以内的整数
UnChannle <- values // 发送到信道
value := <- UnChannle // 从通道中接收信息
// 实现对channel的遍历
(1) 使用for_if来进行遍历
(2) 使用for_range方式来进行遍历
var c = make(chan int)
go func(){
for i:=0; i<100; i++ {
c <- i
}
close(c)
}() // 调用自己
for v := range(c){
fmt.Printf("v: %v\n", v)
}
三、 实现协程同步 (WaitGroup)
需要导入包:
import “sync”
当我们的程序中进行多个协程时,有时往往会使我们的子协程还未完全执行时,主协程就已经调用,而使得部分子协程的功能没有显示出来。此时就需要我们加入同步机制来保证程序的执行。
var swg sync.WaitGroup
func test(i int){
defer swg.Done() // 等待协程结束,登记为-1; 与wg.Add(-1)的功能一样
fmt.Printf("hello 子协程: %v\n", i+1)
}
func main(){
for i:=0; i<10; i++ {
swg.Add(1) // 启动一个协程goroutine登记为+1
go test(i)
}
swg.Wait() //等待所有的协程结束后才执行
}
四、使用runtime包
runtime包中用来协调协程的运行,主要通过Gosched()方法来实现
Gosched() 主要是 " 牺牲自己,奉献别人 ", 把自己的CPU资源转让给别的协程来执行, 最后才执行自己。
当遇到某些条件,需要退出某个协程时,可以执行runtime.Goexit() 方法来退出当前协程。
可以使用 runtime.GOMAXPROCS(i) 来设置CPU的数量,当i=1时,也想要实现协程并行,则需要对每个协程设置上睡眠 time.Sleep(time.MillSecond * 100)
func test(str string){
for i:=0; i<10; i++ {
fmt.Println(s)
}
}
func main(){
go test("GO") // 子协程
// 主协程
for i:=0; i<2; i++ {
runtime.Gosched() // 转让CPU资源
fmt.Println("golang")
}
}