字节池(byte pool)
在go语言中,字节池是一种用来管理和复用字节切片的内存池。使用字节池可以减少内存分配和垃圾回收的压力,尤其是在频繁使用内存的场景中,如网络编程、文件操作和高并发服务中。
概念学习
什么是字节切片
字节切片(Byte Slice)通常是指用于表示一段连续的字节数组的结构。其核心思想在于提供对自己序列的灵活访问和操作
其中,字节指数据存储的基本单位,切片指从一段数据结构中提取出的一部分内容。
在很多编程语言中,字节切片是用来处理原始二进制数据的一种方式,字节切片通常没有固定长度,可以动态调整。
什么是字节池
字节池是一种内存管理机制,通常用于优化内存的分配和释放,尤其是在高频率的内存操作中,它通过预先分配一块内存池,并跟进需求从池中获取或释放内容,避免了频繁的进行内存分配和释放操作。
字节池的工作原理主要是预先分配一块大内存,当程序需要内存时,从池中取出一定大小的内存,而不是每次都从操作系统中申请,当内存使用完后,返回自己池中等待下一次使用
基本实现
在了解了基本概念后,就可以进入go语言的字节池学习。
go语言中使用的是sync.Pool来创建字节池。
package main
import (
"fmt"
"sync"
)
var bytePool = sync.Pool{
// New 是 sync.Pool 中的一个方法,用来定义获取对象的方式。
New: func() interface{} {
// 每次从池中获取不到对象时会调用这个方法来创建新的对象
return make([]byte, 1024) // 返回一个大小为 1024 字节的切片
},
}
func main() {
// 从字节池中获取一个字节切片
buf := bytePool.Get().([]byte)
// 使用 buf 做一些操作
fmt.Printf("buf length: %d\n", len(buf))
// 使用完字节切片后,放回池中复用
bytePool.Put(buf)
}
在这段代码中主要有以下3个阶段:
- sync.Poll():提供了一个机制来池化对象,以减少内存的分配和垃圾回收,它的New函数定义了当池为空时应该如何创建新对象
- bytePoll.Get():从池中获取一个字节切片,如果池中没有对象,New函数会被用来创建一个新的对象
- bytePool.Put(buf):将使用过的字节切片放回池中
工作池(Worker Pool)
在go语言中,工作池是一种常见的并发模式,用于管理和调整多个工作任务,通过工作池可以控制并发工作任务的数量,避免过多goroutine同时执行导致系统资源损耗或性能下降。
当然我们也可以将工作池理解为线程池,其主要作用也差不多。
基本实现
package main
import (
"fmt"
"sync"
"time"
)
// 定义任务的类型,任务可以是一个简单的函数
type Task struct {
ID int
}
// 工作池中的工作函数
func worker(id int, tasks <-chan Task, wg *sync.WaitGroup) {
defer wg.Done() // 完成后通知 WaitGroup
for task := range tasks {
// 模拟任务处理
fmt.Printf("Worker %d processing task %d\n", id, task.ID)
time.Sleep(1 * time.Second) // 模拟任务处理的时间
}
}
// 创建工作池函数
func createWorkerPool(numWorkers int, tasks <-chan Task) *sync.WaitGroup {
var wg sync.WaitGroup
// 启动工作 goroutine
for i := 1; i <= numWorkers; i++ {
wg.Add(1)
go worker(i, tasks, &wg)
}
return &wg
}
func main() {
// 创建一个任务队列
tasks := make(chan Task, 10)
// 创建工作池,假设池中有 3 个工作 goroutine
wg := createWorkerPool(3, tasks)
// 模拟生成任务并添加到任务队列中
for i := 1; i <= 10; i++ {
tasks <- Task{ID: i}
}
// 关闭任务队列,表示没有更多的任务
close(tasks)
// 等待所有工作 goroutine 完成
wg.Wait()
}
这段代码先定义了一个Task类型,其代表一个类型,每个任务有一个ID
然后工作函数worker是每个工作goroutine执行的内容,每个工作goroutine从任务对了中获取任务并处理,直到队列为空
createWorkerPool函数主要用于接受工作的goroutine的数量numWorkers和任务队列tasks,并启动对应数量的工作goroutine来并发执行任务
然后使用一个有缓存tasks作为任务队列,并将任务放入队列中,goroutine从队列中取出任务进行执行