当前位置: 首页 > article >正文

golang 并发编程,每日一题

题目

有三个需要并发执行的函数,三个函数会返回同一类型的值,
且三个函数执行时间在2ms到10ms之间不等,而主程序要求在5ms内返回结果,
若5ms内没有执行完毕,则强制返回结果,这个时候某个函数可能还没有返回因此没有值。

func searchText(query string) ResultText
func searchImage(query string) ResultText
func searchVideo(query string) ResultText

代码

要点在注释中备注

package test

import (
	"context"
	"fmt"
	"math/rand"
	"testing"
	"time"
)

type ResultText string
type ResultImage string
type ResultVideo string
type AllRes struct {
	ResultText
	ResultImage
	ResultVideo
}

func TestConcur(t *testing.T) {
	ctx, cancelFun := context.WithTimeout(context.Background(), time.Millisecond*5)
	defer cancelFun()
	textCh := make(chan ResultText)
	imageCh := make(chan ResultImage)
	videoCh := make(chan ResultVideo)
	go searchText(ctx, "text", textCh)
	go searchImage(ctx, "image", imageCh)
	go searchVideo(ctx, "video", videoCh)
	res := AllRes{}

	for {
		select {
		case textRes := <-textCh:
			res.ResultText = textRes
		case imageRes := <-imageCh:
			res.ResultImage = imageRes
		case videoRes := <-videoCh:
			res.ResultVideo = videoRes

		case <-ctx.Done():
			fmt.Println(res)
			return // 实际业务中,应该在这里返回数据
		}
	}

}

func searchText(ctx context.Context, query string, ch chan ResultText) ResultText {

	res := ResultText("")
	select {
	case <-ctx.Done():
		fmt.Errorf("超时")
		ch <- ""
		return res
	default:
		// 耗时操作,业务逻辑,实际业务逻辑应该封装为一个函数,不写在这个部分,这个部分主要做超时控制
		duration := time.Duration(rand.Intn(9) + 2)
		time.Sleep(time.Millisecond * duration)
		res = ResultText(query + duration.String())
		ch <- res
	}

	return res
}

func searchImage(ctx context.Context, query string, ch chan ResultImage) ResultImage {

	res := ResultImage("")
	select {
	case <-ctx.Done():
		fmt.Errorf("超时")
		ch <- ""
		return res
	default:
		// 耗时操作,业务逻辑,实际业务逻辑应该封装为一个函数,不写在这个部分,这个部分主要做超时控制
		duration := time.Duration(rand.Intn(9) + 2)
		time.Sleep(time.Millisecond * duration)
		res = ResultImage(query + duration.String())
		ch <- res
	}

	return res
}
func searchVideo(ctx context.Context, query string, ch chan ResultVideo) ResultVideo {

	res := ResultVideo("")
	select {
	case <-ctx.Done():
		fmt.Errorf("超时")
		ch <- ""
		return res
	default:
		// 耗时操作,业务逻辑,实际业务逻辑应该封装为一个函数,不写在这个部分,这个部分主要做超时控制
		duration := time.Duration(rand.Intn(9) + 2)
		time.Sleep(time.Millisecond * duration)
		res = ResultVideo(query + duration.String())
		ch <- res
	}
	return res
}

代码CR优化:

  1. 避免重复代码
    searchText、searchImage 和 searchVideo 函数中的代码几乎完全相同,除了类型不同。可以将公共逻辑提取成一个通用的函数,减少代码重复,提高可维护性。

  2. 错误处理
    fmt.Errorf(“超时”) 在 select 中并没有保存错误信息,只是打印出来。由于 select 中的 ctx.Done() 通道已经处理了超时信号,可以直接返回并结束函数,不需要再创建额外的错误信息。

  3. 通道关闭
    searchText、searchImage、searchVideo 中,错误情况下会向通道发送一个空字符串 ch <- “”,但这样会让调用方(TestConcur)收到一个无效的结果。更好的方式是确保只有有效的结果才通过通道发送,或者使用 select 返回一个默认值而不是空字符串。

  4. 随机时间的生成
    随机时间 rand.Intn(9) + 2 的生成是硬编码的,建议将这些常量值提取为变量或常量,以便后期修改。

  5. AllRes 中的结果字段赋值
    在 TestConcur 中,select 中的每个分支直接赋值给 res.ResultText、res.ResultImage 和 res.ResultVideo,这样会导致一旦有一个结果返回,其他通道可能会被阻塞。应该考虑提前判断超时逻辑,避免在业务处理逻辑中阻塞。

  6. 上下文取消和超时
    context 的取消机制已经处理得很好,但为了提高代码可读性和灵活性,可以把超时设置提取为常量,而不是直接硬编码。

package main

import (
	"context"
	"fmt"
	"math/rand"
	"testing"
	"time"
)

type ResultText string
type ResultImage string
type ResultVideo string

type AllRes struct {
	ResultText
	ResultImage
	ResultVideo
}

const maxDuration = 10  // 用于生成随机等待时间的常量

func TestConcur(t *testing.T) {
	ctx, cancelFun := context.WithTimeout(context.Background(), time.Millisecond*5)
	defer cancelFun()

	// 创建通道
	textCh := make(chan ResultText)
	imageCh := make(chan ResultImage)
	videoCh := make(chan ResultVideo)

	// 启动并发任务
	go search(ctx, "text", textCh)
	go search(ctx, "image", imageCh)
	go search(ctx, "video", videoCh)

	// 存储结果
	res := AllRes{}

	for {
		select {
		case textRes := <-textCh:
			res.ResultText = textRes
		case imageRes := <-imageCh:
			res.ResultImage = imageRes
		case videoRes := <-videoCh:
			res.ResultVideo = videoRes
		case <-ctx.Done():
			fmt.Println(res) // 输出结果
			return // 退出,实际业务中可以返回数据
		}
	}
}

// 通用的搜索函数
func search(ctx context.Context, query string, ch interface{}) {
	// 随机生成操作耗时
	duration := time.Duration(rand.Intn(maxDuration) + 2)
	select {
	case <-ctx.Done(): // 超时退出
		fmt.Println("超时:", query)
		return
	default :
		// 根据 query 决定返回的结果类型
		switch v := ch.(type) {
		case chan ResultText:
			v <- ResultText(query + duration.String())
		case chan ResultImage:
			v <- ResultImage(query + duration.String())
		case chan ResultVideo:
			v <- ResultVideo(query + duration.String())
		}
	}
}



http://www.kler.cn/a/420869.html

相关文章:

  • 在 MacOS 上为 LM Studio 更换镜像源
  • Python几种常用数据结构(重制版)
  • 前端开发 之 15个页面加载特效中【附完整源码】
  • Redis使用场景-缓存-缓存雪崩
  • IDEA中更改了项目模块名,IDEA丢失该模块的问题
  • 08-模块与包
  • chrome使用问题记录
  • 【CSP CCF记录】202305-1第30次认证 重复局面
  • 服务器如何划分空间?
  • 【IC验证】systemverilog_函数和任务
  • 杨辉三角形-多语言
  • 气象数据下载服务平台(二)
  • 超文本协议之HTML(泷羽sec)
  • Mysql读写分离分库分表
  • MySQL中的幻读问题
  • Linx下自动化之路:JDK1.8 安装包一键安装脚本实现离线极速部署
  • 高校数字化运营平台解决方案:构建统一的服务大厅、业务平台、办公平台,助力打造智慧校园
  • alpaca 和 sharegpt微调数据格式区别
  • 「Mac畅玩鸿蒙与硬件41」UI互动应用篇18 - 多滑块联动控制器
  • 汽车用聚芳醚砜行业全面而深入的分析
  • WPF从本地文件加载界面
  • Modbus rtu转profibusDP接电机保护器快速配置案例
  • 【代码随想录|贪心算法03】
  • 【Trick】adb指令运行时出现 Error: Activity class {xxx} does not exist.
  • 学习笔记048——Java字节流
  • Kotlin 协程的异常处理