1. goroutine
1.1 goroutine 是什么
goroutine的概念类似于线程,是go语言的核心, goroutine是由Go的运行时(runtime)调度和管理的。
1.2goroutine 的使用
go 中使用 goroutine非常的简单,只需要在函数的前面加上go关键字,就可以为一个函数创建一个 goroutine。
1.3 代码 demo
package main
import (
"fmt"
"time"
)
func sayHello() {
fmt.Println("hello")
}
func main() {
//开启goroutine 使用 go 关键字
go sayHello()
fmt.Println("main end")
//主线程睡一秒
time.Sleep(time.Second)
}
2. goroutine 同步
2.1 使用chan 来进行 goroutine 同步
代码 demo
package main
import "fmt"
//使用 chan 来进行 goroutine 同步
func sayHello(c chan int) {
fmt.Println("hello")
c <- 1
}
func main() {
//初始换一个没有缓冲区的管道
c := make(chan int)
go sayHello(c)
fmt.Println("main end")
<-c
}
2.3 sync.WaitGroup 来实现 goroutine 的同步
sync.WaitGroup 三个方法 Add()、Done()、Wait()。
代码 demo
package main
import (
"fmt"
"sync"
)
//声明 sync.WaitGroup
var wg sync.WaitGroup
func sayHello(i int) {
//
defer wg.Done()
fmt.Printf("%d say hello\n", i)
}
func main() {
for i := 0; i < 10; i++ {
wg.Add(1)
go sayHello(i)
}
wg.Wait()
}
3. channel
channel 是一种类型,一种引用类型,像一个传送带或者队列,遵循先入先出的规则,保证收发数据的顺序 。用于goroutine间的数据交换。
channel 的声明:var 变量名 chan 类型(var c chan int ),使用 make 初始化,close 关闭。
3.1 无缓存区通道
package main
func main() {
//无缓冲区通道
c := make(chan int)
c <- 1
}
上面的代码运行会报错 :fatal error: all goroutines are asleep - deadlock!
无缓存区的通道必须有人取数据才能存数据。
修改上面的代码,添加一个取数据的 goroutine。
package main
import "fmt"
func getChanData(c chan int) {
data := <-c
fmt.Printf("get data :%d\n", data)
}
func main() {
//无缓冲区通道
c := make(chan int)
//启动一个goroutine 获取管道中的数据
go getChanData(c)
//关闭管道
defer close(c)
c <- 1
fmt.Printf("end main!")
}
3.2 有缓存的通道
可以直接存放数据,但数据的个数不能超过缓存区的大小。
package main
import "fmt"
func main() {
//声明一个缓存区为10 的 int 管道
c := make(chan int, 10)
for i := 0; i < 10; i++ {
//向管道存放数据
c <- i
}
fmt.Printf("end main")
}
3.3 单向通道
chan <- 表示单向存数据管道。
<-chan 表示单向取数据管道。
代码 demo
package main
import (
"fmt"
"sync"
)
var wg sync.WaitGroup
//取数据
func onlyReadChan(c <-chan int) {
defer wg.Done()
data := <-c
//如果在代码中加入下面的代码,在编译的时候会出现 invalid operation: c <- 12 (send to receive-only type <-chan int)
//c <- 12
fmt.Printf("get data: %d\n", data)
}
//写数据
func onlyWriteChan(c chan<- int) {
defer wg.Done()
c <- 10
}
func main() {
c := make(chan int)
defer close(c)
wg.Add(2)
go onlyReadChan(c)
go onlyWriteChan(c)
wg.Wait()
fmt.Printf("end main\n")
}
3.4 遍历通道中的值
普通的for循环需要自己判断是不是没有值了。
package main
import "fmt"
func main() {
c := make(chan int, 10)
//存数据
for i := 0; i < 10; i++ {
c <- i
}
//存完值后要关闭通道,不关闭的话,循环取值的时候会 deadlock
close(c)
//取数据
for {
i, ok := <-c
if !ok {
break
}
fmt.Printf("i :%d\n", i)
}
}
用for range 获取,range 就不需要自己判断,所以推荐用这个方式进行遍历。
package main
import "fmt"
func main() {
c := make(chan int, 10)
//存数据
for i := 0; i < 10; i++ {
c <- i
}
//存完值后要关闭通道,不关闭的话,循环取值的时候会 deadlock
close(c)
for v := range c {
fmt.Printf("v is %d\n", v)
}
}