栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Go语言

golang中通知协程退出的方式

Go语言 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

golang中通知协程退出的方式

1 需求分析

go语言中通知子 goroutine 退出的三种方式

  • 方式1 通过全局变量:如果全局变量为真就退出
  • 方式2 通过通道:协程在通道里面取到true就退出
  • 方式3 通过context:通过调用ctx.Done()方法通知所有的协程退出
  • context.WithTimeout超时退出
2 通过全局变量和通道方式

全局变量方式存在的问题:

  1. 使用全局变量在跨包调用时不容易统一
  2. 如果worker中再启动goroutine,就不太好控制了。

管道方式存在的问题:

使用全局变量在跨包调用时不容易实现规范和统一,需要维护一个共用的channel

package main

import (
	"fmt"
	"sync"
	"time"
)

//全局变量
var stopLabel bool

//全局通道
var stopChannel chan bool

var wg sync.WaitGroup

func test01() {
	defer wg.Done()
	for {
		fmt.Println("test01")
		time.Sleep(time.Millisecond * 10)

		//如果全局变量stopLabel为真就退出
		if stopLabel {
			break
		}
	}
}

func test02() {
	defer wg.Done()

	//通过loop的标志位终止
loop:
	for {
		fmt.Println("test02")
		time.Sleep(time.Millisecond * 30)

		
		select {
		case <-stopChannel:
			break loop
		default:
		}
	}
	//不可下面这种写法,chan是阻塞的
	// for {
	// 	fmt.Println("test02")
	// 	time.Sleep(time.Millisecond * 30)

	// 	if <-stopChannel {
	// 		fmt.Println("test02退出")
	// 		break
	// 	}
	// }
}

//方式1 通过全局变量
func globalExit() {
	wg.Add(1)
	go test01()

	//5s以后就停止
	time.Sleep(time.Second * 5)
	stopLabel = true
	wg.Wait()
}

//方式2 通过通道
func channelExit() {
	//初始化通道
	stopChannel = make(chan bool, 1)
	wg.Add(1)
	go test02()

	//5s以后就停止
	time.Sleep(time.Second * 5)
	stopChannel <- true
	wg.Wait()
}

func main() {
	//go语言中通知子 goroutine 退出的三种方式

	//方式1 通过全局变量
	globalExit()
	fmt.Println("方式1 通过全局变量 的方法结束")

	//方式2 通过通道
	channelExit()

	//方式3 通过context
}

3 方式3 通过context

context的优势在于:

当子goroutine又开启另外一个goroutine时,只需要将ctx传入即可

package main

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

var wg sync.WaitGroup

//第2个协程
func test04(ctx context.Context) {
	defer wg.Done()
loop:
	for {
		fmt.Println("test04")
		time.Sleep(time.Millisecond * 10)

		// 等待上级通知
		select {
		case <-ctx.Done():
			break loop
		default:
		}
	}

}

//第一个协程
func test03(ctx context.Context) {
	defer wg.Done()
	//第一个协程调用第2个协程 两个协程都会收到ctx的信号而终止
	go test04(ctx)

loop:
	for {
		fmt.Println("test03")
		time.Sleep(time.Millisecond * 10)

		// 等待上级通知
		select {
		case <-ctx.Done():
			break loop
		default:
		}
	}

}

//方式3 通过context
func contextExit() {
	wg.Add(2) //2个协程在跑
	ctx, cancel := context.WithCancel(context.Background())
	go test03(ctx)

	//5s以后就停止
	time.Sleep(time.Second * 3)
	cancel() // 通知子goroutine结束

	wg.Wait()
}

func main() {
	//go语言中通知子 goroutine 退出的三种方式
	//方式3 通过context
	contextExit()
	fmt.Println("方式3 通过context 结束")
}

4 context.WithTimeout超时退出

取消此上下文将释放与其相关的资源,因此代码应该在此上下文中运行的操作完成后立即调用cancel,通常用于数据库或者网络连接的超时控制。具体示例如下:

package main

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

var wg sync.WaitGroup

func test02(ctx context.Context) {
	defer wg.Done()

	//通过loop的标志位终止
loop:
	for {
		//处理的事务
		fmt.Println("test02")
		time.Sleep(time.Millisecond * 30)

		
		select {
		case <-ctx.Done():
			break loop
		default:
		}
	}

}

//超时退出
func timeoutExit() {
	//3s就超时
	timeLimit := time.Second * 3
	ctx, cancel := context.WithTimeout(context.Background(), timeLimit)
	wg.Add(1)
	go test02(ctx)

	//5s以后再回收相关联的资源
	time.Sleep(time.Second * 5)
	cancel()
	wg.Wait()
}

func main() {

	//超时退出
	timeoutExit()
	fmt.Println("超时退出 的方法结束")

}

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/903065.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号