go context

在Go里, 我们不能直接杀死协程, 协程的关闭一般会用channel+select方式/或者runtime.Goexit()来控制. 虽然能打到目的,但是如果在复杂派生场景下,就会显得乏力了. 于是就引出了Context:

context接口

1.分类

	1.可被控制类
		1.emptyCtx{backgroundCtx,todoCtx} ps:不可取消,没有设置截止时间,没有携带任何值的Ctx
	2.不可被控制类
		1.cancelCtx{cancelCtx}
		2.timerCtx{deadlineCtx,timeoutCtx}
		3.valueCtx{valueCtx}

2.接口

	1.context接口(所有ctx都实现了)
		1.deadline方法
			返回ctx的deadline,以及ctx是否会被取消;没有设置deadline的话,ok==false
		2.done方法
			ctx被取消后,返回一个被关闭的channel
		3.err方法
			返回ctx被取消的原因
		4.value方法
			返回参数key对应的value
	2.canceler接口(只有cancelCtx和timerCtx实现了)
		实现了这个接口表明该ctx是可以被取消的
		# context接口

四个方法:

type Context interface {
	Deadline() (deadline time.Time, ok bool)
	Donne() <-chan struct{}
	Err() error
	Value(key interface{}) interface{}
}

1.Deadline()
	func (context.Context).Deadline() (deadline time.Time, ok bool)
		deadline:	返回当前Context被取消的时间
		ok:			如果没有设置deadline,则返回ok==false: 不会被取消

2. Done()
	func (context.Context).Done() <-chan struct{}
		context被取消的信号(是否执行cancelFunc):
			1. 执行cancelFunc						(即 context被取消):
					将返回一个关闭的channel(),select读取分支从这个关闭的channel读取除了零值(nil)
					读取到了零值就代表context被关闭,于是就执行退出协程.
			2. 不执行cancelFunc						(即context不会被取消):
					将返回一个nil(什么也不返回,更没有channel),
					返回一个nil就代表没有channel可以读, 那么select就直接进入default分支执行!

3. Err()
	func (context.Context).Err() error
		返回一个错误,表示context被关闭的原因:
			1. context未被关闭,返回nil
			2. context被关闭,返回一个非nil错误:
				2.1.Context被取消 -- Canceled;
				2.2.Context超时   -- DeadlineExceeded;

4. Value()
	获取之前设置的key所对应的value

用法

1.创建根ctx

	根ctx的类型:
		1.context.Background()获取background类型ctx
		2.context.Todu()获取todo类型ctx

2.创建子ctx

	子ctx的类型:
		1.caccelCtx
			1.withcancel
				实现接口: context,canceler
				创建方法:context.WithCancel(父Ctx)(withcancelCtx, 当前ctx的取消函数)
		2.timerCtx(timerCtx基于cancelCtx,只是多了一个time.Timer和一个deadline)
			1.deadlineCtx
				实现接口: context,canceler
				创建方法:context.WithDeadline(父Ctx)...
			2.timeoutCtx
				实现接口: context,canceler
				创建方法:context.WithTimeout(父Ctx)...
		3.valueCtx
			1.valueCtx
				实现接口: context
				创建方法:context.WithValue(父Ctx)...

案例

package main

import (
	"context"
	"fmt"
	"time"
)

/*
func context.WithCancel(parent context.Context) (ctx context.Context, cancel context.CancelFunc)
	参数:
		parent:	父Context,可以使用background或者TODO作为根Context!
	返回值:
		ctx:	返回根据父Context创建的子Context
		cancel:	取消函数

*/

func main() {
	// 1. 根ctx
	ctx0 := context.Background()

	// 2. 创建子Ctx,类型为withcancel
	ctx1, cancel1 := context.WithCancel(ctx0)

	// 3. 传入子withcancel到go1协程中
	go go1(ctx1)

	// 4. 创建孙子Ctx,类型为withcancel
	ctx2, cancel2 := context.WithCancel(ctx1)
	_ = cancel2

	// 5. 传入孙子withcancel到go2协程中
	go go2(ctx2)

	// 6. 取消ctx1
	time.Sleep(time.Second * 22)
	cancel1() // 当父Context关闭的时候,其派生的所有子Context也会被关闭!

	// 7.查看ctx1被关闭的原因
	err := ctx1.Err()
	if err != nil {
		fmt.Printf("%v: levelOne_context被关闭的原因:%v\n", time.Now().UTC().Unix(), err)
	}
}

// go1
func go1(ctx context.Context) {
	count := 1
	for {
		select {
		case <-ctx.Done(): // 接收ctx关闭信号
		default: // 执行任务
			time.Sleep(time.Second * 5)
			fmt.Printf("%v: levelOne_goroutine正在执行%d次任务!\n", time.Now().UTC().Unix(), count)
			count++
		}
	}
}

// go2
func go2(ctx context.Context) {
	count := 1
	for {
		select {
		case <-ctx.Done(): // 返回关闭的chan --> 关闭当前协程 (啥都不需要做,goroutine会自动关闭)
		default: //               返回nil		 --> 执行goroutine的任务
			time.Sleep(time.Second * 5)
			fmt.Printf("%v: levelTwo_gotoutine正在执行%d次任务!\n", time.Now().UTC().Unix(), count)
			count++
		}
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值