Go Context包之cancelCtx实现

涉及的两个重要基础知识点:

    1. 关闭的channel里面读取数据默认永远有个零值兜底

    2. select里面加上default就不会阻塞

其一:

package main

import "fmt"

func main() {
	ch:= make(chan int)
	close(ch)
	for {
		select {
		case v :=<-ch:
			fmt.Println("value",v)
		}
	}
}


//结果(关闭的channel永远能读到值)
value 0
value 0
value 0
value 0
value 0
value 0
value 0
......

 其二:

package main

import "fmt"

func main() {
	ch:= make(chan  int)
	for{
		select {
		case v:=<-ch:
			fmt.Println("value:",v)
			default:  //不加default会阻塞报错:fatal error: all goroutines are asleep - deadlock!
		}
	}
}

cancelCtx实现:

package realize

import "sync"

//必备知识:
//从开启的channel读数据会阻塞,但从关闭的channel里面读取数据,依然可以接受到之前已经成功发送的数据;如果channel中已经没有数据的话将产生一个零值的数据,这个零值在关闭的channel里面是无限的,可以一直读到
//实现原理
//cancel的实现原理,根节点创建一个channel,在每个子gorountine中进行channel接收的select监控(注意此处需要有default,不然会造成阻塞系统报错),
//当关闭channel时,cancnel channel就会因为接收到默认零值而进入select,此时便可以进行关闭等操作。

//1.定义信号源context
//2.传递信号源并在gorountine监控
//3.信号源关闭
//4.gorountine关闭
var closedchan = make(chan struct{})

func init() {
	//开启一个关闭的channel
	close(closedchan)
}
type Context struct {
	mu sync.Mutex
	done chan struct{}//关闭信号
}

func NewCancelContext() Context {
	return Context{}
}
//发送关闭信号
func (ctx *Context)Cancel(){
	ctx.mu.Lock()
	if ctx.done == nil{
		//cancel方法在done()方法之前执行,此时还没生成channel,故直接赋值一个关闭的channel(这里是cancelcontext的精髓)
		ctx.done = closedchan
	}else{
		//cancel方法在done()方法之后执行,已经在done方法里面生成了channel,只需关闭即可
		close(ctx.done)
	}
	ctx.mu.Unlock()
}
//返回channel,懒创建
func (ctx *Context)Done() <-chan struct{} {
	ctx.mu.Lock() //必须加锁,防止
	if ctx.done == nil{
		ctx.done = make(chan struct{})
	}
	ctx.mu.Unlock()
	return ctx.done
}

调用案例跟context包的cancelCtx调用一一致

package main

import (
	"exercise/context/realize"
	"fmt"
	"sync"
	"time"
)
var wg sync.WaitGroup
func main() {

	ctx := realize.NewCancelContext()
	wg.Add(1)
	go work(ctx)
	//time.Sleep(time.Second*2)
	ctx.Cancel()//直接调用可能会会出现panic: close of nil channel,所以cancle方法里面有特殊的处理开启一个关闭的channel
	wg.Wait()
}
func work(ctx realize.Context)  {
	LOOP:
	for{
		fmt.Println("worker")
		time.Sleep(time.Second*1)
		select {
			case <-ctx.Done():
				fmt.Println("退出work")
				break LOOP
		default: //必须要设置default,因为没有channel的输入,只有接收和关闭,不设置default会造成select阻塞死锁
		}
	}
	wg.Done()
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

koral chen

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值