优化一个已有的 Go 程序,提高其性能并减少资源占用,把实践过程和思路整理成文章 | 青训营

62 阅读2分钟

假设我们有一个任务队列,其中包含需要进行处理的任务。我们的目标是使用goroutine并行处理这些任务,同时优化性能和资源占用。

初始版本:

package main

import (
	"fmt"
	"sync"
)

func processTask(task int) {
	// 模拟任务处理,这里可能是一些耗时的操作
	fmt.Printf("Processing task %d\n", task)
}

func main() {
	tasks := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}

	var wg sync.WaitGroup

	for _, task := range tasks {
		wg.Add(1)
		go func(t int) {
			defer wg.Done()
			processTask(t)
		}(task)
	}

	wg.Wait()
	fmt.Println("All tasks completed")
}

这个初始版本的程序使用了goroutine来并行处理任务。但是,它存在一些问题:

  1. 无限制的并发:虽然我们使用了goroutine来并行处理任务,但是它们没有受到任何限制,可能导致大量的goroutine被同时创建,消耗过多的内存和CPU资源。
  2. 任务调度:由于没有对并发数量进行限制,goroutine的调度可能导致过多的上下文切换,影响性能。

优化版本:

下面是一个优化版本的示例,我们将引入任务限制、任务池和使用sync.Pool来复用对象:

package main

import (
	"fmt"
	"sync"
)

const maxConcurrent = 4 // 限制并发的最大数量

var taskPool = sync.Pool{
	New: func() interface{} {
		return make(chan int, 1)
	},
}

func processTask(task int) {
	// 模拟任务处理,这里可能是一些耗时的操作
	fmt.Printf("Processing task %d\n", task)
}

func main() {
	tasks := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}

	var wg sync.WaitGroup
	concurrent := make(chan struct{}, maxConcurrent) // 控制并发数量的通道

	for _, task := range tasks {
		wg.Add(1)
		concurrent <- struct{}{} // 增加并发计数

		go func(t int) {
			defer wg.Done()
			defer func() {
				taskPool.Put(<-concurrent) // 减少并发计数
			}()

			processTask(t)
		}(task)
	}

	wg.Wait()
	fmt.Println("All tasks completed")
}

在这个优化版本中,我们引入了一个taskPool,使用sync.Pool来复用通道,以减少内存分配和回收的开销。同时,我们通过限制并发数量,使用通道concurrent来控制同时处理的任务数量,避免了无限制的并发。这样,我们在保持一定的并发控制的同时,也能降低不必要的上下文切换。 在这个示例中,强调了对并发数量的限制、资源复用和任务调度的优化。实际优化过程中,还需要考虑更多方面,如内存管理、IO操作、算法优化等。不同的应用场景和需求可能需要不同的优化策略,因此需要根据实际情况进行调整和测试。