• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    公众号

Go控制并发

原作者: [db:作者] 来自: [db:来源] 收藏 邀请

// 控制并发有三种经典的方式:一种是WaitGroup,另外一种是Chan通知,还有一种是Content.

// WaitGroup
// WaitGroup适用于好多个Goroutine协同做一件事,每个Goroutine只做这件事的一部分。
func main1() {
var wg sync.WaitGroup

wg.Add(2)

go func() {
time.Sleep(2 * time.Second)
fmt.Println("1号完成")
wg.Done()
}()

go func() {
time.Sleep(2 * time.Second)
fmt.Println("2号完成")
wg.Done()
}()

wg.Wait()
fmt.Println("都完成,结束")
}

// Chan 通知
// Goroutine启动以后我们是无法控制它的,大部分情况是等待它自己结束,如果这个Goroutine是一个不会自己结束的后台Goroutine?
// 使用全局变量,后台Goroutine去不停检查是否被通知关闭
// 使用Chan + select
// 弊端:如果有很多Goroutine都需要控制结束怎么办?这些Goroutine又衍生了其他的Goroutine怎么办,一层层的无穷尽的Goroutine,非常复杂!

func main2() {
stop := make(chan bool)
go func() {
for {
select {
case <-stop:
fmt.Println("停止了")
return
default:
fmt.Println("进行中")
time.Sleep(2 * time.Second)
}
}
}()

time.Sleep(10 * time.Second)
fmt.Println("通知进行停止")
stop <- true

// 是否停止
time.Sleep(5 * time.Second)
}

// Context Goroutine的上下文
// 使用 Context 跟踪 Goroutine,以便进行控制,优雅解决了Goroutine启动后不可控的问题

// 使用Context控制一个Goroutine
func main3() {
background := context.Background() // 返回一个空的Context,这个空的Context一般用于整个Context树的根节点
ctx, cancel := context.WithCancel(background) // 创建一个可取消的子Context,传递给Goroutine使用,使用子Context跟踪这个Goroutine
go func(ctx context.Context) {
for {
select {
case <-ctx.Done():
fmt.Println("停止了")
return
default:
fmt.Println("进行中")
time.Sleep(2 * time.Second)
}
}
}(ctx)

time.Sleep(10 * time.Second)
fmt.Println("通知进行停止")
cancel() // 调用它发送取消指令,然后Goroutine会接收到信号

// 查看是否结束
time.Sleep(5 * time.Second)
}

// 使用Context控制多个Goroutine
func main4() {
ctx, cancel := context.WithCancel(context.Background())

go do(ctx, "1号")
go do(ctx, "2号")
go do(ctx, "3号")

time.Sleep(10 * time.Second)
fmt.Println("通知进行停止")
cancel() // 所有基于这个Context或者衍生的子Context都会收到通知

// 查看是否结束
time.Sleep(5 * time.Second)
}

func do(ctx context.Context, name string) {
for {
select {
case <-ctx.Done():
fmt.Println(ctx.Err())
fmt.Println(name + "停止了")
return
default:
fmt.Println(name + "进行中")
time.Sleep(2 * time.Second)
}
}
}

// WithValue传递元数据
var key string = "name"

func main() {
ctx, cancel := context.WithCancel(context.Background())
// 附加值
valueCtx := context.WithValue(ctx, key, "1号")

go watch2(valueCtx)

time.Sleep(10 * time.Second)
fmt.Println("通知进行停止")
cancel()
time.Sleep(5 * time.Second)
}

func watch2(ctx context.Context) {
for {
select {
case <-ctx.Done():
// 取出值
fmt.Println(ctx.Value(key), "停止了")
return
default:
// 取出值
fmt.Println(ctx.Value(key), "进行中")
time.Sleep(2 * time.Second)
}

}
}

// Context 使用原则
// 不要把Context放在结构体中,要以参数的方式传递
// 以Context作为参数的函数方法,应该把Context作为第一个参数,放在第一位。
// 给一个函数方法传递Context的时候,不要传递nil,如果不知道传递什么,就使用(context.TODO)
// Context的Value相关方法应该传递必须的数据,不要什么数据都使用这个传递
// Context是线程安全的,可以放心的在多个goroutine中传递

鲜花

握手

雷人

路过

鸡蛋
该文章已有0人参与评论

请发表评论

全部评论

专题导读
上一篇:
Go语言微服务框架实战:4.gRPC介绍和安装发布时间:2022-07-10
下一篇:
【go】go国内镜像发布时间:2022-07-10
热门推荐
热门话题
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap