eleasetime = cputicks()
}
gp := sg.g
gp.param = unsafe.Pointer(sg)
sg.success = false
if raceenabled {
raceacquireg(gp, c.raceaddr())
}
glist.push(gp)
}
unlock(&c.lock)
// 为所有被阻塞的 Goroutine 调用 runtime.goready 触发调度。
for !glist.empty() {
gp := glist.pop()
gp.schedlink = 0
goready(gp, 3)
}
使用场景
报错情形
- 往一个关闭的channel发送数据会报错:panic: send on closed channel
- 关闭一个nil的chan会报错:panic: close of nil channel
- 关闭一个已经关闭的channel报错:panic: close of closed channel
1、一个经典的算法题
有4个goroutine,编号为1、2、3、4。每秒钟会有一个goroutine打印出自己的编号,要求写一个程序,让输出的编号总是按照1、2、3、4、1、2、3、4...的顺序打印出来
package main
import (
"fmt"
"time"
)
func main() {
// 4个channel
chs := make([]chan int, 4)
for i, _ := range chs {
chs[i] = make(chan int)
// 开4个协程
go func(i int) {
for {
// 获取当前channel值并打印
v := <-chs[i]
fmt.Println(v + 1)
time.Sleep(time.Second)
// 把下一个值写入下一个channel,等待下一次消费
chs[(i+1)%4] <- (v + 1) % 4
}
}(i)
}
// 往第一个塞入0
chs[0] <- 0
select {}
}
2、限流器
package main
import (
"fmt"
"time"
)
func main() {
// 每次处理3个请求
chLimit := make(chan struct{}, 3)
for i := 0; i < 20; i++ {
chLimit <- struct{}{}
go func(i int) {
fmt.Println("下游服务处理逻辑...", i)
time.Sleep(time.Second * 3)
<-chLimit
}(i)
}
time.Sleep(30 * time.Second)
}
如果觉得sleep太丑太暴力,可以用waitGroup控制结束时机
package main
import (
"fmt"
"sync"
"time"
)
var wg sync.WaitGroup
func main() {
// 每次处理3个请求
chLimit := make(chan struct{}, 3)
for i := 0; i < 20; i++ {
chLimit <- struct{}{}
wg.Add(1)
go func(i int) {
fmt.Println("下游服务处理逻辑...", i)
time.Sleep(time.Second * 3)
<-chLimit
wg.Done()
}(i)
}
wg.Wait()
}
3、优雅退出
package main
import (
"fmt"
"log"
"os"
"os/signal"
"syscall"
"time"
)
func main() {
var closing = make(chan struct{})
var closed = make(chan struct{})
go func() {
for {
select {
case <-closing:
return
default:
fmt.Println("业务逻辑...")
time.Sleep(1 * time.Second)
}
}
}()
termChan := make(chan os.Signal)
// 监听退出信号
signal.Notify(termChan, syscall.SIGINT, syscall.SIGTERM)
<-termChan
// 退出中
close(closing)
// 退出之前清理一下
go doCleanup(closed)
select {
case <-closed:
case <-time.After(time.Second):
log.Println("清理超时不等了")
}
log.Println("优雅退出")
}
func doCleanup(closed chan struct{}) {
time.Sleep(time.Minute)
// 清理完后退出
close(closed)
}
4、实现互斥锁
初始化一个缓冲区为1的channel,放入元素代表一把锁,谁获取到这个元素就代表获取了这把锁,释放锁的时候再把这个元素放回channel
package main
import (
"log"
"time"
)
type Mutex struct {
ch chan struct{}
}
// 初始化锁
func NewMutex() *Mutex {
mu := &Mutex{make(chan struct{}, 1)}
mu.ch <- struct{}{}
return mu
}
// 加锁,阻塞获取
func (m *Mutex) Lock() {
<- m.ch
}
// 释放锁
func (m *Mutex) Unlock() {
select {
// 成功写入channel代表释放成功
case m.ch <- struct{}{}:
default:
panic("unlock of unlocked mutex")
}
}
// 尝试获取锁
func (m *Mutex) TryLock() bool {
select {
case <-m.ch:
return true
default:
}
return false
}
func (m *Mutex) LockTimeout(timeout time.Durati