goroutine
它是go并发设计的核心。
goroutine就是协程,它比线程更小,十几个goroutine在底层可能就是五六个线程。
go语言内部实现了goroutine的内存共享,执行goroutine只需极少的栈内存(大概是4~5KB)。
main函数是主协程,如果主协程退出其他任务也不会执行,程序直接退出。
runtime包
runtime.Gosched()
runtime.Gosched()用于让出CPU时间片,调度器会重新进行任务调度,但是有可能还是分配到该任务
package main
import (
"fmt"
"runtime"
)
func main() {
//匿名子协程
go func(s string) {
for i := 0; i < 2; i++ {
fmt.Println(s)
}
}("world")
//主协程
for i := 0; i < 2; i++ {
runtime.Gosched()
fmt.Println("hello")
}
}
runtime.Goexit()
立即终止当前协程,但是会保证已经注册的defer延迟调用都被执行。
package main
import (
"fmt"
"time"
"runtime"
)
func main() {
//匿名子协程
go func() {
defer fmt.Println("A.defer")
//匿名函数
func() {
defer fmt.Println("B.defer")
//此时只有defer执行
runtime.Goexit()
fmt.Println("B")
}()
fmt.Println("A")
}()
for {
time.Sleep(time.Second)
}
}
该程序执行只会输出
B.defer
A.defer
runtime.GOMAXPROCS()
设置并发执行的CPU个数并返回之前的值
package main
import (
"runtime"
"fmt"
)
func main() {
n := runtime.GOMAXPROCS(3)
fmt.Println("n=%d\n",n)
//循环执行2个
for{
go fmt.Print(0)
fmt.Print(1)
}
}
runtime. NumCPU()
返回系统的CPU数量
runtime. NumGoroutine()
返回正在执行和排队的所有任务总数
runtime.GOOS
返回操作系统
channel
创建
channel可以用内置make()函数创建
make(chan 类型) //无缓冲的通道,阻塞
make(chan 类型, 容量) //有缓冲的通道,非阻塞
传输数据
channel <- value //发送value到channel
<-channel //接收通道数据,并丢弃
x := <-channel //通道取值并赋给x
x, ok := <-channel //ok是检查通道是否关闭或者是否为空
遍历
for foundOre := range c {
time.Sleep(2 * time.Second)
fmt.Println("Miner: Received " + strconv.Itoa(foundOre) + " from finder")
}
遍历会阻塞直到有新数据发送到chanel,并且在遍历成功后会继续遍历
关闭
close(c)
select
go语言提供了select关键字,可以监听channel上的数据流动
语法与switch类似,区别是select要求每个case语句里必须是一个IO操作
select {
case <-chan1:
// 如果chan1成功读到数据,则进行该case处理语句
case chan2 <- 1:
// 如果成功向chan2写入数据,则进行该case处理语句
default:
// 如果上面都没有成功,则进入default处理流程,如果没有default流程则select会阻塞直到有一个case成功
}
sync包
Sync包同步提供基本的同步原语。
golang 并不推荐这个包中的大多数并发控制方法,但还是提供了相关方法,主要原因是golang中提倡以共享内存的方式来通信。
sync.Mutex
互斥锁
var l sync.Mutex
l.Lock()
l.Unlock
sync.RWMutex
读写锁
同时只能有一个 goroutine 能够获得写锁定。
同时可以有任意多个 gorouinte 获得读锁定。
同时只能存在写锁定或读锁定(读和写互斥)。
var l sync.RWMutex
l. RLock() // 加读锁
l. RUnlock() //加写锁
l. Lock()//加写锁
l. Unlock()//解写锁
sync. WaitGroup
WaitGroup可以用来等待一组goroutine结束
func (wg *WaitGroup) Add(delta int)//增加delta个,注意,wg.Add() 方法一定要在 goroutine 开始前执行
func (wg *WaitGroup) Done() //计数-1
func (wg *WaitGroup) Wait() //等待结束
sync.Cond
条件变量
var locker = new(sync.Mutex)
var cond = sync.NewCond(locker)
cond. Broadcast() // 唤醒所有挂起的goroutine
cond.Signal() //唤醒1个goroutine
cond.Wait() //必须要获取锁才能调用,Wait方法在调用时会释放底层锁Locker,并且将当前goroutine挂起,直到另一个goroutine执行Signal或者Broadcase,该goroutine才有机会重新唤醒,并尝试获取Locker,完成后续逻辑
sync.Once
保证只执行一次
var once sync.Once
onceBody := func() {
fmt.Println("Only once")
}
done := make(chan bool)
for i := 0; i < 10; i++ {
go func() {
once.Do(onceBody)
done <- true
}()
}
for i := 0; i < 10; i++ {
<-done
}