文章

Go-知识并发控制Channel

channel 作为协程间通信的类型,用来在协程间传递信息是基本能力,而通过灵活的使用 channel ,可以实现各种各样复杂的并发控制。

Go-知识并发控制Channel

文章信息

  • 原文链接:https://jiayq.blog.csdn.net/article/details/138972958
  • 发布时间:2024-05-16 19:38:45
  • 阅读量:588
  • 分类:Golang专栏收录该内容, 订阅专栏
  • 标签:#golang, #go, #go 并发管理, #chan, #channel

摘要

文章浏览阅读588次,点赞5次,收藏7次。channel 作为协程间通信的类型,用来在协程间传递信息是基本能力,而通过灵活的使用 channel ,可以实现各种各样复杂的并发控制。


Go-知识并发控制Channel

  • 1. 无缓冲 chan 实现 并发控制
  • 2. 有缓冲 chan 实现 并发控制
  • 3. 总结

薅羊毛:Baidu Comate,200积分换20元京东E卡
给你分享一个AI编码助手——百度Comate!扫码参与抽红包活动,520宠粉!送京东卡!https://comate.baidu.com/zh/shopping?inviteCode=y21fpcef
薅羊毛:Baidu Comate,200积分换20元京东E卡

channel 一般用于协程之间的通信,不过channel也可以用于并发控制。 比如主协程启动N个子协程,主协程等待所有子协程退出后再继续后续流程,这种并发控制的场景,channel也可以实现。

channel 的用法: Go 知识chan

1. 无缓冲 chan 实现 并发控制

假设要在主协程中创建10个子协程,主协程等待子协程执行完成后主协程在退出。
Go 知识for-range

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
func TestCc(t *testing.T) {
    // 定义子协程个数
	sum := 10
	// 创建10个 chan 用于通信
	chs := make([]chan struct{}, sum)
	for i := 0; i < sum; i++ {
	    // 创建 chan
		ch := make(chan struct{})
		chs[i] = ch
		// 因为在 子协程中用到了 chan 和 index ,所以使用临时变量拷贝一下, for - range 
		// 更多细节: https://jiayq.blog.csdn.net/article/details/135886263
		ti := i
		go func() {
		    // 子协程打印消息
			name := fmt.Sprintf("goroutine %d", ti)
			fmt.Println(name, "start")
			// 子协程睡眠模拟耗时逻辑
			time.Sleep(3 * time.Second)
			fmt.Println(name, "end")
			// 发送子协程完成和退出信号
			ch <- struct{}{}
			// 关闭资源
			close(ch)
		}()
	}
	// 在主协程中读取子协程的退出信号,阻塞从 chan 中读取数据
	for _, ch := range chs {
		<-ch
	}
	fmt.Println("all goroutine done")
}

执行结果如下:
在这里插入图片描述

和预期一致。

2. 有缓冲 chan 实现 并发控制

还是和1相同的逻辑,区别在于上面是一个子协程就有1个 chan ,假设有1w的子协程,就需要创建1w的chan。
而有缓冲的 chan 则只需要一个即可:
Go-知识协程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
func TestCc1(t *testing.T) {
    // 主协程中创建10个子协程
	sum := 10
	// 创建 带有 10 个 缓冲空间的 chan
	// 缓冲空间的大小和需要管理的子协程的大小应该相同,否则子协程如果执行比较快,就会因为缓冲满而阻塞
	// 其实子协程因为缓冲满阻塞,也没有关系,依赖 go 的高效 调度,当出现因为 chan 阻塞时,长时间无法得到资源,
	// 就会让 绑定 G 的 P 脱离 M , 等到 chan 有空间了在继续执行
	// 更多细节: https://jiayq.blog.csdn.net/article/details/137087910
 	ch := make(chan struct{}, sum)
	for i := 0; i < sum; i++ {
	    // 闭包使用
		ti := i
		go func() {
			name := fmt.Sprintf("goroutine %d", ti)
			fmt.Println(name, "start")
			time.Sleep(3 * time.Second)
			fmt.Println(name, "end")
			ch <- struct{}{}
		}()
	}
	for i := 0; i < sum; i++ {
		<-ch
	}
	close(ch)
	fmt.Println("all goroutine done")
}

执行结果
在这里插入图片描述

上面的程序即使不带缓冲,也是能实现并发控制的:
在这里插入图片描述

3. 总结

channel 作为协程间通信的类型,用来在协程间传递信息是基本能力,而通过灵活的使用 channel ,可以实现各种各样复杂的并发控制。

本文由作者按照 CC BY 4.0 进行授权