首页 > 其他分享 >go chan阻塞实例

go chan阻塞实例

时间:2023-08-01 12:23:17浏览次数:26  
标签:int32 chan 发送 缓冲区 实例 go 接收 通道

以下的代码段在执行写入通道的时候会发生阻塞:

    spaceId2badgeDatasChan := make(chan map[int32][]*badgeV1.BadgeData)
    spaceId2badgeCountChan := make(chan map[int32]int32)

	var getBadgesTasks []func()
	for _, loopSpaceId := range spaceIds {
		task := func(spaceId int32) func() {
			return func() {
				req := &badgeV1.GetRequest{SpaceIds: []int32{spaceId}}
				getRes, err := uc.badgeClt.Get(ctx, req)
				if err != nil {
					return
				}
				spaceId2badgeDatasChan <- map[int32][]*badgeV1.BadgeData{spaceId: getRes.Data.DataList}
				spaceId2badgeCountChan <- map[int32]int32{spaceId: getRes.Data.Count}
			}
		}(loopSpaceId)
		getBadgesTasks = append(getBadgesTasks, task)
	}

	// 执行任务
	utils.RunParallelTasks(getBadgesTasks...)
	
	// 读取数据
	close(spaceId2badgeCountChan)
	close(spaceId2badgeDatasChan)
	spaceId2badgeDatas := make(map[int32][]*badgeV1.BadgeData)
	spaceId2badgeCount := make(map[int32]int32)
	for m := range spaceId2badgeDatasChan {
		for spaceId, datas := range m {
			spaceId2badgeDatas[spaceId] = datas
		}
	}
	for m := range spaceId2badgeCountChan {
		for spaceId, count := range m {
			spaceId2badgeCount[spaceId] = count
		}
	}

这段代码意图先通过并发执行多任务,获取到数据,然后统一采集数据,但是在代码执行到这两行的时候就卡住了:

spaceId2badgeDatasChan <- map[int32][]*badgeV1.BadgeData{spaceId: getRes.Data.DataList}
spaceId2badgeCountChan <- map[int32]int32{spaceId: getRes.Data.Count}

由于没有设置 spaceId2badgeDatasChanspaceId2badgeCountChan 通道的缓冲区大小,这可能会导致通道在发送数据时被阻塞,因为通道的接收方无法及时读取数据。你可以在通道初始化时指定缓冲区大小来解决这个问题,例如: 

	spaceId2badgeDatasChan := make(chan map[int32][]*badgeV1.BadgeData, len(spaceIds))
	spaceId2badgeCountChan := make(chan map[int32]int32, len(spaceIds))
	var getBadgesTasks []func()
	for _, loopSpaceId := range spaceIds {
		task := func(spaceId int32) func() {
			return func() {
				req := &badgeV1.GetRequest{SpaceIds: []int32{spaceId}}
				getRes, err := uc.badgeClt.Get(ctx, req)
				if err != nil {
					return
				}
				spaceId2badgeDatasChan <- map[int32][]*badgeV1.BadgeData{spaceId: getRes.Data.DataList}
				spaceId2badgeCountChan <- map[int32]int32{spaceId: getRes.Data.Count}
			}
		}(loopSpaceId)
		getBadgesTasks = append(getBadgesTasks, task)
	}

	// 执行任务
	utils.RunParallelTasks(getBadgesTasks...)
	
	// 读取数据
	close(spaceId2badgeCountChan)
	close(spaceId2badgeDatasChan)
	spaceId2badgeDatas := make(map[int32][]*badgeV1.BadgeData)
	spaceId2badgeCount := make(map[int32]int32)
	for m := range spaceId2badgeDatasChan {
		for spaceId, datas := range m {
			spaceId2badgeDatas[spaceId] = datas
		}
	}
	for m := range spaceId2badgeCountChan {
		for spaceId, count := range m {
			spaceId2badgeCount[spaceId] = count
		}
	}

chan的接收和发送需要能同时进行,任何一端都不能卡住

 

通道的缓冲区大小对通道的性能和行为有很大的影响,尤其是在处理大量数据时。如果通道的缓冲区大小过小,发送方可能会被阻塞,因为缓冲区已满,而接收方无法及时读取数据。如果缓冲区大小过大,可能会导致内存浪费和性能下降。

在之前代码中,由于 spaceId2badgeCountChan 的缓冲区大小不够大,发送方在发送数据时被阻塞,而接收方无法及时读取数据。这导致发送方一直等待接收方读取数据,而接收方却无法读取数据,从而导致了阻塞。

通过调整缓冲区大小,可以解决这个问题。如果能够确定通道需要缓存多少数据,可以直接设置缓冲区大小,例如:

spaceId2badgeCountChan := make(chan map[int32]int32, len(spaceIds))

在这个例子中,我将缓冲区大小设置为 len(spaceIds),这样通道就可以缓存所有可能的数据,以避免通道被阻塞。

如果无法确定通道需要缓存的数据量,可以使用无缓冲通道或有缓冲通道。无缓冲通道可以确保发送方和接收方同步,即发送方会等待接收方读取数据,而接收方会等待发送方发送数据。这种方式适用于需要确保发送和接收的顺序的情况。例如:

spaceId2badgeCountChan := make(chan map[int32]int32)

在这个例子中,使用了无缓冲通道,这意味着发送方会等待接收方读取数据,而接收方会等待发送方发送数据。这可以确保发送和接收的顺序正确。

另一种方法是使用有缓冲的通道,这可以避免发送方被阻塞,同时也可以确保发送和接收的顺序正确。你可以根据需要设置缓冲区大小,例如:

spaceId2badgeCountChan := make(chan map[int32]int32, 10)

在这个例子中,我将缓冲区大小设置为 10,这意味着通道可以缓存最多 10 个元素,如果缓冲区已满,发送方会被阻塞。同时,由于有缓冲的通道是异步的,因此发送方不必等待接收方读取数据,这可以提高程序的并发性能。

 

标签:int32,chan,发送,缓冲区,实例,go,接收,通道
From: https://www.cnblogs.com/zhanchenjin/p/17596126.html

相关文章

  • 跳表的原理--Golang 实现一个简单跳表
    前言最近在看《Redis设计与实现》这本书,书中简单描述了跳表的性质和数据结构,但对它的具体实现没有多讲。书里对跳表结构的描述是这样的:跳跃表节点:typedefstructzskiplistNode{//后退指针structzskiplistNode*backward;//分值doublescore;//......
  • go 循环变量捕获 陷阱
    以下这样的循环代码,最后会发现spaceId都是同一个!!!! for_,spaceId:=rangespaceIds{ task:=func(){ uc.log.WithContext(ctx).Errorf("SpaceUsecase::GetSpacesuc.badgeClt.GetspaceId:%vstart!!!!",spaceId) } getBadgesTasks=append(getBadgesTasks,task)......
  • OpenHarmony系统解决方案 - 接入多个显示设备卡开机Logo
    问题描述问题环境系统版本:OpenHarmony-3.2-Release问题现象接入多个显示设备后,启动系统偶现卡开机Logo。异常效果:系统卡在开机Logo界面,长时间无法显示开机动画,并且无法进入系统。正常效果:系统启动成功,显示开机动画,开机动画结束后显示锁屏界面。问题原因在窗口子系统中Abstra......
  • could not import go.etcd.io/etcd/clientv3-go
    问题描述今天在封装etcd的时候导包报错:couldnotimportgo.etcd.io/etcd/clientv3(norequiredmoduleprovidespackage"go.etcd.io/etcd/clientv3")问题解决:get:确保下载了client包gogetgo.etcd.io/etcd/clienttidygomodtidy本文由mdnice多平台发布......
  • django 简单文件上传
    通过模型来处理上传的文件¶如果想要在 FileField 上的 Model 保存文件,使用 ModelForm 会让这一过程变得简单。当调用 form.save() 时,文件对象将会被保存在对相应 FileField 的 upload_to 参数所指定的地方:fromdjango.httpimportHttpResponseRedirectfromdja......
  • 生产消费模型-使用channel控制生产速度
    packagemainimport("fmt""sync")typeProducerstruct{IDintDataStreamchanintWaitGroup*sync.WaitGroup}func(p*Producer)Produce(concurrency<-chanstruct{}){deferp.WaitGroup.Done(......
  • go操作kafka
    go操作kafkaZooKeeper是一个分布式协调服务,它的主要作用是为分布式系统提供一致性服务,提供的功能包括:配置维护、命名服务、分布式同步、组服务等。Kafka的运行依赖ZooKeeper。目前kafka3.2.0以上版本(kafka_2.13-3.2.0.tgz)内就包含自带的ZooKeeper,因此直接下载Kafka就行。解......
  • 【go语言】3.1.2 接口的定义和实现
    在Go中,接口是一种抽象类型,用来描述其他类型应该有哪些方法。它定义了一组方法,但没有实现。这些方法由其他类型实现。接口的定义接口定义的格式如下:typeInterfaceNameinterface{Method1(param1type1,param2type2)returntype1Method2(param1type1,param2ty......
  • MongoDB数据库的部署和应用
    推荐步骤:在Centos01上部署MongoDB服务器客户端登录验证在centos01的MongoDB配置文件通过配置文件控制MongoDB服务,配置MongoDB身份验证在centos01的MongoDB服务器配置身份验证管理和修改配置文件支持验证在centos01管理MongoDB管理数据,集合批量数据管理实验步骤创建管理MongoDB组和......
  • Django 动态操作model
    fromdjango.appsimportappsforoinoids_result:oid_result=snmp.snmpWalk(o.oid)ifo.model_filed_nameandlen(o.model_filed_name)>0:field_name=o.model_filed_name.get('field_......