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

Go 不可重复协程安全队列

代码实现

package dataStruct

import (
	"errors"
	"sync"
)

// GenericQueue 是一个支持泛型的不可重复队列,具有最大长度限制
// T 是泛型参数
type GenericQueue[T comparable] struct {
	items   map[T]struct{} // 使用 map 来存储元素
	order   []T            // 使用切片来记录元素的顺序
	lock    sync.RWMutex   // 读写锁,保证并发安全
	maxSize int            // 最大队列长度
}

// NewGenericQueue 创建一个新的泛型队列,并指定最大长度
func NewGenericQueue[T comparable](maxSize int) *GenericQueue[T] {
	return &GenericQueue[T]{
		items:   make(map[T]struct{}), // 初始化 items map
		order:   []T{},                // 初始化 order 切片
		maxSize: maxSize,              // 设置最大长度
	}
}

// Add 添加元素到队列
// 如果队列已满,默认移除最旧的元素来腾出空间
func (q *GenericQueue[T]) Add(item T) error {
	q.lock.Lock()         // 加写锁,防止并发修改
	defer q.lock.Unlock() // 解锁

	// 判断元素是否已经存在
	if _, exists := q.items[item]; exists {
		return errors.New("item already exists in the queue")
	}

	// 如果队列已满,移除最旧的元素
	if len(q.order) >= q.maxSize {
		return errors.New("Out of length Queue ")
	}

	// 将元素添加到 map 中,标记该元素已存在
	q.items[item] = struct{}{}
	// 将元素添加到队列顺序中
	q.order = append(q.order, item)
	return nil
}

// Remove 删除队列中的指定元素
// 如果元素不存在,则返回错误
func (q *GenericQueue[T]) Remove(item T) error {
	q.lock.Lock()         // 加写锁,防止并发修改
	defer q.lock.Unlock() // 解锁

	// 判断元素是否存在
	if _, exists := q.items[item]; !exists {
		return errors.New("item not found in the queue")
	}

	// 从 map 中删除该元素
	delete(q.items, item)

	// 从顺序切片中删除该元素
	for i, v := range q.order {
		if v == item {
			q.order = append(q.order[:i], q.order[i+1:]...) // 删除指定位置的元素
			break
		}
	}
	return nil
}

// Update 修改队列中的元素
// 如果元素不存在,则返回错误
func (q *GenericQueue[T]) Update(oldItem, newItem T) error {
	q.lock.Lock()         // 加写锁,防止并发修改
	defer q.lock.Unlock() // 解锁

	// 判断 oldItem 是否存在
	if _, exists := q.items[oldItem]; !exists {
		return errors.New("old item not found in the queue")
	}

	// 判断 newItem 是否已存在
	if _, exists := q.items[newItem]; exists {
		return errors.New("new item already exists in the queue")
	}

	// 从 map 中删除 oldItem,并添加 newItem
	delete(q.items, oldItem)
	q.items[newItem] = struct{}{}

	// 更新顺序切片中的 oldItem 为 newItem
	for i, v := range q.order {
		if v == oldItem {
			q.order[i] = newItem
			break
		}
	}
	return nil
}

// Contains 查询队列中是否存在指定元素
func (q *GenericQueue[T]) Contains(item T) bool {
	q.lock.RLock()         // 加读锁,防止并发修改
	defer q.lock.RUnlock() // 解锁
	_, exists := q.items[item]
	return exists
}

// Traverse 遍历队列中的元素
func (q *GenericQueue[T]) Traverse() []T {
	q.lock.RLock()         // 加读锁,防止并发修改
	defer q.lock.RUnlock() // 解锁
	// 返回元素的副本,防止并发问题
	result := make([]T, len(q.order))
	copy(result, q.order)
	return result
}

// Iterator 返回一个可以用于 for range 的通道
func (q *GenericQueue[T]) Iterator() <-chan T {
	ch := make(chan T)
	go func() {
		q.lock.RLock()         // 加读锁,防止并发修改
		defer q.lock.RUnlock() // 解锁
		defer close(ch)        // 关闭通道
		for _, item := range q.order {
			ch <- item
		}
	}()
	return ch
}

介绍

1. 支持泛型

  • 队列支持泛型参数 T,因此可以存储任何类型的元素,只要该类型是 comparable(可比较的)。这意味着你可以使用这个队列来存储数字、字符串、结构体等类型的数据。

2. 不可重复

  • 队列内部通过 map 来存储元素,因此它自动确保元素的唯一性。也就是说,队列中不会有重复的元素。

3. 顺序存储

  • 使用切片 order 来记录元素的顺序,保证在遍历时元素的顺序是加入队列时的顺序。

4. 并发安全

  • 队列实现了并发控制,使用了 sync.RWMutex 读写锁来保证在多线程环境下的安全操作。多个 goroutine 可以并发地读取队列,或者一个 goroutine 可以安全地修改队列。

5. 常见操作

  • 添加元素(Add): 如果元素已经存在,则无法再次添加;如果队列已满,返回 Out of length Queue 错误,不会自动移除最旧元素
  • 删除元素(Remove): 可以删除队列中的某个元素。
  • 更新元素(Update): 可以将队列中的某个元素更新为新的元素,前提是新元素不存在。
  • 检查元素是否存在(Contains): 查询队列中是否包含某个元素。
  • 遍历队列(Traverse): 返回队列中所有元素的副本,供遍历使用。
  • 支持 for range 遍历: 通过 Iterator 方法,队列可以返回一个只读通道,可以直接在 for range 循环中使用。

6. 适用场景

  • 适用于需要按顺序处理且确保元素唯一的场景,例如任务队列、事件队列等。
  • 适用于并发环境,确保多个操作的安全性。

示例用法:

 

type Ta struct {
	Name string
}

func main() {
	impl := test.XueTestingImpl
	impl.StartTest()

	// 示例:创建一个支持整数的泛型队列
	queue := dataStruct.NewGenericQueue[Ta](1024)
	queue.Add(Ta{Name: "xzm"})
	queue.Add(Ta{Name: "yyds"})
	queue.Add(Ta{Name: "id"})

	//修改
	queue.Update(Ta{Name: "id"}, Ta{Name: "ids"})

	//遍历
	for ta := range queue.Iterator() {
		fmt.Println(ta)
	}

	//查找
	contains := queue.Contains(Ta{
		Name: "yyds",
	})

	//删除
	queue.Remove(Ta{
		Name: "yyds",
	})

	fmt.Println(contains)

	fmt.Println(queue)
	fmt.Println(queue.Traverse())
	impl.EndTest()
}


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

相关文章:

  • 【博客之星】年度总结:在云影与墨香中探寻成长的足迹
  • 把 PVE 下的机械硬盘(非SSD系统盘)分配给虚拟机使用
  • 虚幻基础-1:cpu挑选(14600kf)
  • Linux中关于glibc包编译升级导致服务器死机或者linux命令无法使用的情况
  • 快速入门Flink
  • 基于微信小程序的健身管理系统设计与实现(LW+源码+讲解)
  • 全同态加密理论、生态现状与未来展望(下)
  • 华为OD机试E卷 --快递投放问题 --24年OD统一考试(Java JS Python C C++)
  • Redis Java 集成到 Spring Boot
  • 修改docker共享内存shm-size
  • 算法基础 -- 红黑树初识
  • 科家多功能美发梳:科技赋能,重塑秀发新生
  • GStreamer 简明教程(九):插件开发,以一个音频特效插件为例
  • 二叉树删除子树 (题目分析+C++代码实现)
  • 基于Java+Springboot+MySQL旅游景区景点网站订票系统设计与实现
  • 【C++拓展】vs2022使用SQlite3
  • OSCP - Proving Grounds - Image
  • 動態住宅IP提升網站訪問成功率
  • 【机器人学】2-3.六自由度机器人运动学逆解-工业机器人【附MATLAB代码】
  • Git:把单个commit合到本地分支
  • cursor把md转换成pdf
  • 电子应用设计方案102:智能家庭AI鱼缸系统设计
  • Redis面试题每日20道【其三】
  • 在宝塔安装部署mindoc
  • C# 使用HttpClient进行Post请求总是出现超时问题的优化
  • 一文了解二叉树的基本概念