文章目录
一、goroutine协程
1、进程
一个运行中的程序称为一个进程,进程是操作系统进行资源分配的基本单位。父进程可以创建多个子进程。子进程复制父线程资源,父子相互独立。
package main
import "os"
func main() {
pid := os.Getpid()
ppid := os.Getppid()
println("进程id:", pid)
println("父进程id:", ppid)
}
2、线程
一个进程可以包含多个线程。进程中的资源被会被进程中的线程共享,线程拥有自己的私有资源。线程是由主线程创建,无需复制进程,节省系统资源。
3、协程
1、协程的创建:协程是轻量级的线程,协程的创建消耗的系统资源更小。运行main函数的协程是主协程,主协程通过 go 关键字创建子协程
package main
import (
"time"
)
func main() {
// 使用go关键字开启一个协程
go println("hello goroutine world")
// 让主协程睡眠1毫秒,等待子协程执行完毕
time.Sleep(time.Millisecond)
}
2、当主协程执行结束时,子协程也会结束。runtime.Gosched()可以让其他协程有机会运行,但并不一定可以得到执行。
package main
import (
"runtime"
)
func main() {
// 使用go关键字开启一个协程
go println("hello goroutine world")
// runtime.Gosched()可以让其他协程有机会运行
runtime.Gosched()
}
3、sync.WaitGroupWait() 主协程等待子协程都执行完毕再结束
package main
import (
"sync"
)
// 协程计数器
var wg sync.WaitGroup
func myGoroutine(i int) {
defer func() {
// 计数器减1
wg.Done()
}()
println("子协程", i)
}
func main() {
for i := 0; i < 10; i++ {
// 计数器加1
wg.Add(1)
go myGoroutine(i)
}
// 阻塞等待直到计数器为0
wg.Wait()
}
4、runtime.Goexit() 结束当前协程
package main
import (
"runtime"
"sync"
)
// 协程计数器
var wg sync.WaitGroup
func main() {
// 计数器加1
wg.Add(1)
go myGoroutine()
// 阻塞等待直到计数器为0
wg.Wait()
}
func myGoroutine() {
defer func() {
println("defer 语句内")
// 计数器减1
wg.Done()
}()
println("goexit前")
// 退出当前协程
runtime.Goexit()
println("goexit后")
}
5、runtime.NumGoroutine() 获取当前协程数量
package main
import (
"runtime"
"sync"
)
var wg sync.WaitGroup
func main() {
println("当前活跃协程数量", runtime.NumGoroutine())
for i := 0; i < 10; i++ {
wg.Add(1)
go func() {
defer wg.Done()
println("当前活跃协程数量", runtime.NumGoroutine())
}()
}
wg.Wait()
}
二、channel通道
协程函数的返回值不可以通过变量直接接受,需要通过通道channel传递。
1、只读通道
只读channel 只能从channel中读取数据,不能写入。读取空通道会阻塞当前协程。
func main() {
// 读取空通道会阻塞当前协程
go myReadGoroutine()
time.Sleep(time.Second * 10)
}
func myReadGoroutine() {
// 定义一个只读通道
c := make(<-chan int)
// 读取空通道会阻塞当前协程
i := <-c
// c <- 1 只读通道不能写入
println(i)
}
2、只写通道
只写通道不能读取。当写入通道无充足空间时会阻塞当前协程。
package main
import (
"math/rand"
"time"
)
func main() {
go myWriteGogoutine()
time.Sleep(time.Second * 10)
}
func myWriteGogoutine() {
// 定义一个只写通道
c := make(chan<- int)
// 获取随机数
i := rand.Int()
// 随机数写入通道
c <- i
// i = <-c 只写通道不能读取
}
2、双向通道
双向通道即可读也可写
package main
import "math/rand"
func main() {
// 定义一个双向通道
c := make(chan int)
for i := 0; i < 10; i++ {
// 启动10个子协程向通道写入数据
go myWriteGogoutine(c)
}
for i := 0; i < 10; i++ {
// 主协程读取通道数据
myReadGoroutine(c)
}
}
// 参数为只写通道
func myWriteGogoutine(c chan<- int) {
i := rand.Int()
c <- i
}
// 参数为只读通道
func myReadGoroutine(c <-chan int) {
i := <-c
println(i)
}
3、有缓冲通道
1、缓冲区已满时,写入会阻塞当前协程
package main
import "time"
func main() {
go cacheChanel()
time.Sleep(time.Second * 10)
}
func cacheChanel() {
c := make(chan int, 3)
println("开始写入")
c <- 1
println("写入1")
c <- 2
println("写入2")
c <- 3
println("写入3")
c <- 4 // 缓冲区已满,阻塞
println("写入4")
}
2、缓冲区内无数据时读取会阻塞当前协程
package main
import "time"
func main() {
go readCacheChannel()
time.Sleep(time.Second * 5)
}
func readCacheChannel() {
c := make(chan int, 3)
println("开始读取")
println("读取", <-c)
}
4、通道关闭后写入
package main
import "time"
func main() {
go cacheChanel()
time.Sleep(time.Second * 10)
}
func cacheChanel() {
c := make(chan int, 3)
println("开始写入")
c <- 1
println("写入1")
c <- 2
println("写入2")
close(c)
c <- 3 // 通道关闭后写入报错
println("写入3")
}
5、通道关闭后读取
通道关闭后缓冲区无数据读取0值,缓冲区有数据读取缓冲区数据
package main
import "time"
func main() {
go readCacheChannel()
time.Sleep(time.Second * 5)
}
func readCacheChannel() {
c := make(chan int, 3)
c <- 1
println("开始读取")
close(c)
println("读取", <-c)// 1
println("读取", <-c)// 0
}
6、通道的 for range 操作
package main
import "math/rand"
func producer(c chan<- int) {
for {
i := rand.Intn(20)
if i == 5 {
c <- i
close(c)
break
} else {
c <- i
}
}
}
func consumer(c <-chan int) {
// for range 获取channel中的值
for i := range c {
println("接收到来自生产者的值", i)
}
}
func main() {
c := make(chan int)
// 子协程生产者
go producer(c)
// 主协程消费者
consumer(c)
}
7、通道的 select 操作
package main
import (
"math/rand"
"time"
)
func main() {
c1, c2 := make(chan int), make(chan int)
go func() {
for {
i := rand.Intn(5)
c1 <- i
time.Sleep(time.Second * time.Duration(i))
}
}()
go func() {
for {
i := rand.Intn(5)
c2 <- i
time.Sleep(time.Second * time.Duration(i))
}
}()
for {
select {
case i := <-c1:
println("接收到c1数据", i)
case i := <-c2:
println("接收到c2数据", i)
case <-time.After(time.Second):
println("等待超时")
return
}
}
}