首页 > 其他分享 >golang channel 的众多应用场景123

golang channel 的众多应用场景123

时间:2024-07-13 14:51:52浏览次数:20  
标签:2024 00 07 08 golang 123 13T11 channel

目录
我们知道 go 中有个很重要的数据结构叫做 channel-通道,通过其特性,我们可以完成很多功能,自然就对应到很多应用场景了。

1.应用场景

channel 的一些应用场景包括不限于下面的这些:

  • 1.同步,用来在多个协程之间的同步操作,如同一时间,只能有一个协程工作,其他协程只能等待,这里也可以对应到同步互斥的场景
  • 2.数据传递,可通过 channel 在多个协程间进行数据传递,比如在 生产者-消费者 模式中使用,生产者向 channel 发送数据,消费者从 channel 中消费数据,也就涉及到对 channel 写-读 操作
  • 3.管道,channel 作为管道,可将数据从一处转移到其他处,在数据流中常见
  • 4.缓冲,在有缓存的 channel 中发送数据,没有读取的情况下,就可以将数据缓存起来,有助于缓解读者压力
  • 5.信号通知,通过 channel 的读写完成信号通知
  • 6.范围迭代,通过 for val := range chan 的操作,当遇到 channel 关闭的时候就会退出迭代,从而完成 channel 数据所有接收
  • 7.并发控制,通过 channel 的有缓存类型,限制一定的并发数量,不至于无限制并发消耗系统资源
  • 8.定时器,有很多标准库的结构体都带有 channel 功能,比如 time.Timer,就是一个定时器,通过定时器定期执行某些任务

以上是涉及到 channel 一些应用场景,以下是应用场景的一些实现,见下面的代码示例。

2.应用场景示例

2.1 并发控制

package main

import (
	"fmt"
	"time"
)

// 通过限制 limit 的缓存数量,决定并发时有多少协程在并行运行
var limit = make(chan struct{}, 3)

/**
Time: 2024-07-13T11:44:01.9047944+08:00, Goroutine: 0 exec i : 0, v: 1
Time: 2024-07-13T11:44:01.9047944+08:00, Goroutine: 11 exec i : 11, v: 12
Time: 2024-07-13T11:44:01.9047944+08:00, Goroutine: 4 exec i : 4, v: 5
Time: 2024-07-13T11:44:02.9200384+08:00, Goroutine: 8 exec i : 8, v: 9
Time: 2024-07-13T11:44:02.9200384+08:00, Goroutine: 9 exec i : 9, v: 10
Time: 2024-07-13T11:44:02.9201398+08:00, Goroutine: 10 exec i : 10, v: 11
Time: 2024-07-13T11:44:03.9207458+08:00, Goroutine: 5 exec i : 5, v: 6
Time: 2024-07-13T11:44:03.9207458+08:00, Goroutine: 1 exec i : 1, v: 2
Time: 2024-07-13T11:44:03.9207458+08:00, Goroutine: 2 exec i : 2, v: 3
Time: 2024-07-13T11:44:04.9240989+08:00, Goroutine: 6 exec i : 6, v: 7
Time: 2024-07-13T11:44:04.9243366+08:00, Goroutine: 3 exec i : 3, v: 4
Time: 2024-07-13T11:44:04.9243366+08:00, Goroutine: 7 exec i : 7, v: 8
Time: 2024-07-13T11:44:07+08:00, 主线程退出!
 */
func main() {
	tasks := []int{1, 2, 3, 4, 5, 6 ,7, 8, 9, 10, 11, 12}
	for i, v := range tasks {
		// 每个task开启一个协程
		go func(i, v int) {
			// 通过chan控制并发
			limit <- struct{}{}

			// 具体的任务执行
			fmt.Printf("Time: %v, Goroutine: %v exec i : %d, v: %v\n", time.Now().Format(time.RFC3339Nano), i, i, v)
			time.Sleep(time.Second)

			<- limit
		}(i, v)
	}

	time.Sleep(6 * time.Second)
	fmt.Printf("Time: %v, 主线程退出!", time.Now().Format(time.RFC3339))
}

2.2 管道 | 范围迭代 | 数据传输

package main

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

func main() {
	numCh := make(chan int)

	for i := 0; i < 5; i++ {
		go sender(numCh)
	}

	for val := range numCh {
		fmt.Println("Main recv val: ", val)
		time.Sleep(time.Second*2)
	}

	fmt.Println("Data transform model Done.")
}

func sender(num chan int)  {
	randomNum := rand.Intn(100)
	num <- randomNum
	fmt.Println("Send val to chan:", randomNum)
	time.Sleep(time.Second)
}

2.3 数据传递 -> 生产者-消费者模型

package main

import (
	"fmt"
	"math/rand"
	"sync"
	"time"
)

/*
result:
...
生产者持续发送消息:59
消费者持续接收消息:59
生产者持续发送消息:53
生产者关闭通道channel.
消费者持续接收消息:53
消费者接收完成.
本次生产者-消费者模型结束.
 */
func main() {
	var wg sync.WaitGroup
	wg.Add(2)

	numCh := make(chan int, 1)
	go producer(numCh, &wg)
	go consumer(numCh, &wg)

	wg.Wait()
	fmt.Println("本次生产者-消费者模型结束.")
}

func producer(num chan int, wg *sync.WaitGroup)  {
	defer wg.Done()
	count := 0
	for count < 1 {
		for i := 0; i < 3; i++ {
			randomNum := rand.Intn(100)
			num <- randomNum
			fmt.Printf("生产者持续发送消息:%d\n", randomNum)
		}
		time.Sleep(time.Second)
		count++
	}

	close(num)
	fmt.Println("生产者关闭通道channel.")
}

func consumer(num chan int, wg *sync.WaitGroup)  {
	defer wg.Done()
	for val := range num {
		fmt.Printf("消费者持续接收消息:%d\n", val)
		time.Sleep(time.Second)
	}

	fmt.Println("消费者接收完成.")
}

2.4 互斥同步

package main

import (
	"fmt"
	"time"
)

func main() {
	mu := NewMutexLock()
	ok := mu.tryLock()
	fmt.Printf("locked v %v\n", ok) // true
	ok = mu.tryLock()
	fmt.Printf("locked v %v\n", ok) // false,需要等待持锁方释放后才能再获取锁
}

type mutex struct {
	ch chan struct{}
}

func NewMutexLock() *mutex {
	// 定义 1 个 cap 的互斥锁,拿到数据读取到数据就是拿到锁,发送成功数据就是解锁
	mu := &mutex{ch: make(chan struct{}, 1)}
	mu.ch <- struct{}{}
	return mu
}

// 读取到数据就是锁定状态
func (m *mutex) lock()  {
	<- m.ch
}

// 写入数据就是解锁状态
func (m *mutex) unlock()  {
	select {
	case m.ch <- struct{}{}:
	default:
		panic("unlock of unlocked mutex")
	}
}

func (m *mutex) tryLock() bool {
	select {
	case <- m.ch:
		return true
	default:
	}

	return false
}

// 锁超时
func (m *mutex) lockTimeout(timeout time.Duration) bool {
	timer := time.NewTimer(timeout)
	select {
	case <- m.ch:
		timer.Stop()
		return true
	case <- timer.C:
	}

	return false
}

func (m *mutex) isLocked() bool {
	return len(m.ch) == 0
}

2.5 信号通知

package main

import (
	"fmt"
	"os"
	"os/signal"
	"syscall"
	"time"
)

/*
处理业务中...
处理业务中...
处理业务中...
处理业务中...
处理业务中...
超时了,不等cleanup了。
优雅退出。
*/
func main() {
	var (
		closing = make(chan struct{})
		closed = make(chan struct{})
	)

	go func() {
		// 模拟业务处理
		for {
			select {
			case <- closing:
				return
			default: // 只要没有打断程序运行,这里就会一直执行下去,直到收到打断信号
				fmt.Println("处理业务中...")
				time.Sleep(100 * time.Millisecond)
			}
		}
	}()

	// 处理 ctrl + c 等中断信号
	terminalChan := make(chan os.Signal)
	signal.Notify(terminalChan, syscall.SIGINT, syscall.SIGTERM) // 待接收信号写入 terminalChan
	<- terminalChan // 收到信号,读取信号

	close(closing) // 关闭 closing,发送信号给业务处理 goroutine,让其结束业务

	// 因为已经结束业务,这里做一些清理工作
	go doCleanup(closed) // 清理结束后,会关闭 closed chan

	// 主线程监听 closed 的关闭情况
	select {
	case <- closed:
	case <- time.After(time.Second):
		fmt.Println("超时了,不等cleanup了。")
	}

	fmt.Println("优雅退出。")
}

//
func doCleanup(closed chan struct{})  {
	time.Sleep(5 * time.Second) // 模拟实际的清理
	close(closed) // 关闭 chan
}

2.6 定时器

package main

import (
	"fmt"
	"os"
	"os/signal"
	"syscall"
	"time"
)

/*
处理业务中...
处理业务中...
处理业务中...
处理业务中...
处理业务中...
超时了,不等cleanup了。
优雅退出。
*/
func main() {
	var (
		closing = make(chan struct{})
		closed = make(chan struct{})
	)

	go func() {
		// 模拟业务处理
		for {
			select {
			case <- closing:
				return
			default: // 只要没有打断程序运行,这里就会一直执行下去,直到收到打断信号
				fmt.Println("处理业务中...")
				time.Sleep(100 * time.Millisecond)
			}
		}
	}()

	// 处理 ctrl + c 等中断信号
	terminalChan := make(chan os.Signal)
	signal.Notify(terminalChan, syscall.SIGINT, syscall.SIGTERM) // 待接收信号写入 terminalChan
	<- terminalChan // 收到信号,读取信号

	close(closing) // 关闭 closing,发送信号给业务处理 goroutine,让其结束业务

	// 因为已经结束业务,这里做一些清理工作
	go doCleanup(closed) // 清理结束后,会关闭 closed chan

	// 主线程监听 closed 的关闭情况
	select {
	case <- closed:
	case <- time.After(time.Second):
		fmt.Println("超时了,不等cleanup了。")
	}

	fmt.Println("优雅退出。")
}

//
func doCleanup(closed chan struct{})  {
	time.Sleep(5 * time.Second) // 模拟实际的清理
	close(closed) // 关闭 chan
}

标签:2024,00,07,08,golang,123,13T11,channel
From: https://www.cnblogs.com/davis12/p/18300078

相关文章

  • 【Python123题库】#查询省会 #字典的属性、方法与应用
    禁止转载,原文:https://blog.csdn.net/qq_45801887/article/details/140081665参考教程:B站视频讲解——https://space.bilibili.com/3546616042621301有帮助麻烦点个赞~~Python123题库查询省会字典的属性、方法与应用查询省会类型:字典‪‬‪‬‪‬‪‬‪‬‮‬‪‬......
  • 搞懂清结算,只需要记住“123457”
    搞懂清结算其实总结起来就是1234567七个数字。怎么理解?我们来看看分享。前几天看了一篇万字长文,深度解析了支付机构的清结算账务处理原理。总结起来就是“1张图、2条线、3在途、4段数、5账户、7环节”,6去那了,大家可以找找,其中 1张图就是这样一张极简图,基本阐述了整个清结......
  • 易优cms网站field 功能:获取channelartlist标签里的字段值,field标签只能在channelartli
    【基础用法】名称:field功能:获取channelartlist标签里的字段值,field标签只能在channelartlist标签里使用。语法:{eyou:channelartlisttypeid='栏目ID'type='son'row='20'}<ahref='{eyou:fieldname='typeurl'/}'>{eyou:fieldname='typenam......
  • #BAS3123. 【例21.3】 字符类型判断
    3123:【例21.3】字符类型判断【题目描述】输入一个字符,判断该字符是否大写字母、小写字母、数字字符或其他字符。分别输出对应的提示信息。【输入】输入为一个字符。【输出】如果该字符是大写字母,则输出" upper ";若是小写字母,则输出" lower ";若是数字字符,则输出" di......
  • golang 接口按需获取资源
    场景爬虫业务场景,我们需要调用三方接口获取代理ip地址,每个ip地址可以使用的时间有限和价格的,本着不浪费资源,我们在这里做一层封装。当有其他业务调用我们接口的时候,会拉起定时任务,这个定时任务的生命周期为5分钟,超过5分钟这个定时任务就会停止,每一次请求时都会更新定时生命周期......
  • YOLOv10改进 | 独家创新- 注意力篇 | YOLOv10引入结合SimAM和Channel Attention形成全
    1.CSimAM介绍     CSimAM(ChannelSimAM)注意力机制结合了SimAM和通道注意力机制(ChannelAttention),在图像特征提取上展现出比单独使用SimAM更为优异的性能。以下是详细描述:     SimAM注意力机制     SimAM(SimilarityAttentionMechanism)通过计......
  • golang sync.Pool 的基本原理
    sync.Pool包寥寥不过300行代码,却可以为我们提供类似对象池的功能,减少了对象的重复创建于销毁,减少了性能损耗,增加内存复用,同时自带mutex锁可以保证Put/Get操作的并发安全特性,在一些对象需要重复创建销毁的场景中很是实用,今天来看看sync.Pool的基本原理。sync.Pool就在标准......
  • go并发模式 o-channel
    packagemainimport("fmt""time")funcmain(){varorfunc(channels...<-chaninterface{})<-chaninterface{}or=func(channels...<-chaninterface{})<-chaninterface{}{switchlen(channels)......
  • go并发模式 or-do-channel + bridge
    packagemainimport("context""fmt")//orDonefuncorDone(ctxcontext.Context,value<-chanint)<-chanint{ordoneStream:=make(chanint)gofunc(){deferclose(ordoneStream)for{......
  • go并发模式 tee-channel
    packagemainimport("context""fmt""time")functeeChannel(ctxcontext.Context,value<-chanint)(<-chanint,<-chanint){ch1:=make(chanint)ch2:=make(chanint)gofunc(){......