欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 汽车 > 维修 > Go中定时任务讲解、context.WithCancel机制的理解

Go中定时任务讲解、context.WithCancel机制的理解

2024/10/25 10:27:25 来源:https://blog.csdn.net/weixin_45863010/article/details/140908573  浏览:    关键词:Go中定时任务讲解、context.WithCancel机制的理解

定时任务

        之前对于定时任务的理解感觉是一个很高大尚的东西,需要比较复杂的代码实现。最近做需求接触到了定时任务在go中的实现,突然觉得对于定时任务的理解比较片面了。

        定时任务最简单的理解就是每隔一定时间执行某种任务,根据上述理解最简单的实现莫过于使用sleep机制实现,每隔一定时间让当前线程、协程进行休眠,从而达到定时的效果。

 

        除了Sleep方式实现的定时任务外,还可以使用select和timeAfter()、实现定时任务:

context.WithCancel机制理解

        Golang中context包提供上下文机制在 goroutine 之间传递 deadline、取消信号(cancellation signals)或者其他请求相关的信息。

        其中context.WithCancel 函数能够从 context.Context 中衍生出一个新的子上下文并返回用于取消该上下文的函数。一旦我们执行返回的取消函数,当前上下文以及它的子上下文都会被取消,所有的 Goroutine 都会同步收到这一取消信号。

Demo

// 父母在家小娃就得学习,小娃每隔1s告诉他父母 “I am working!”,
// 但是过了5s后父母出门了,小娃就没必要学习了,于是开始 “playing”package mainimport ("context""fmt""time"
)func dosomething(ctx context.Context) {for {select {case <-ctx.Done():fmt.Println("playing")returndefault:fmt.Println("I am working!")time.Sleep(time.Second)}}
}func main() {ctx, cancelFunc := context.WithCancel(context.Background())go func() {time.Sleep(5 * time.Second)cancelFunc()}()dosomething(ctx)
}

 

        这里基于根上下文context.Background(),生成了一个带取消函数cancelFunc的子上下文ctx(可以理解为是:父母在家,小娃基于父母在家这个前提一直在学习),通过调用这个cancelFunc,释放一个信号(父母出门)结束基于这个子上下文的工作(还学个甚),也就是从ctx.Done()这个channel获取到值,执行了return操作。

Cancel()机制

        从一个已经关闭的channel中可以一直获取到对应的零值。

package mainimport ("fmt""time"
)func main() {//新建一个通道ch,其传递的数据类型为intch := make(chan int, 10)//往通道里传入5个整数for i := 0; i < 5; i++ {ch <- i}//关闭通道close(ch)timeout := time.After(10 * time.Second)for {select {case <-timeout:fmt.Println("\ntimeout")returncase out := <-ch:fmt.Printf("%d", out)time.Sleep(time.Second)}}
}

可以看到当传入的01234依次被取出后依旧取出了int类型的零值

回到context,先看下WithCancel函数的定义,最后返回的是子上下文和一个cancelFunc函数,而cancelFunc函数里调用了cancelCtx这个结构体的方法cancel。

func WithCancel(parent Context) (ctx Context, cancel CancelFunc) {if parent == nil {panic("cannot create context from nil parent")}c := newCancelCtx(parent)propagateCancel(parent, &c)return &c, func() { c.cancel(true, Canceled) }
}// newCancelCtx returns an initialized cancelCtx.
func newCancelCtx(parent Context) cancelCtx {return cancelCtx{Context: parent}
}

注意到cancelCtx这个结构体,字段done是一个传递空结构体类型的channel,用来在上下文取消时关闭这个通道,err就是在上下文被取消时告诉用户这个上下文取消了,可以用ctx.Err()来获取信息。

type cancelCtx struct {Contextmu       sync.Mutex            // protects following fieldsdone     chan struct{}         // created lazily, closed by first cancel callchildren map[canceler]struct{} // set to nil by the first cancel callerr      error                 // set to non-nil by the first cancel call
}

到这里问题的关键就在cancel函数,先看定义,cancel函数取消了基于该上下文的所有子上下文以及把自身从父上下文中取消。

// cancel closes c.done, cancels each of c's children, and, if
// removeFromParent is true, removes c from its parent's children.
func (c *cancelCtx) cancel(removeFromParent bool, err error) {...if c.done == nil {c.done = closedchan} else {close(c.done)}...
}
从上面的代码close(c.done)就可以看到通过执行cancel函数将c.done通道关闭了,也就是demo里的ctx.Done()通道(ps:可以简单看下Done方法的定义)
func (c *cancelCtx) Done() <-chan struct{} {c.mu.Lock()if c.done == nil {c.done = make(chan struct{})}d := c.donec.mu.Unlock()return d
}

总结


1、从一个被close的channel中接收数据不会被阻塞,而是立即返回,接收完已发送的数据后会返回传递的元素类型的零值(zero value)
2、ctx, cancelFunc := context.WithCancel(context.Background()) 通过执行cancelFunc函数关闭chan struct{}通道,从而可以从ctx.Done()获取返回值{},最后结束该上下文。

参考链接

【Golang | Context】context.WithCancel()取消机制的理解-CSDN博客

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com