资源管理方法
- 使用goroutine池:
- 原理:通过预先创建一定数量的goroutine,重复使用这些goroutine来处理任务,避免频繁创建和销毁goroutine带来的开销。例如在一个Web服务器中,处理大量HTTP请求时,如果每个请求都创建新的goroutine,可能导致资源耗尽。使用goroutine池可以限制同时运行的goroutine数量,有效管理资源。
- 示例代码:
package main
import (
"fmt"
"sync"
)
// Worker 定义工作者
type Worker struct {
id int
jobs <-chan int
wg *sync.WaitGroup
}
// Start 启动工作者
func (w *Worker) Start() {
defer w.wg.Done()
for job := range w.jobs {
fmt.Printf("Worker %d processing job %d\n", w.id, job)
}
}
// Pool 定义goroutine池
type Pool struct {
workers []*Worker
jobs chan int
}
// NewPool 创建新的goroutine池
func NewPool(numWorkers, jobCapacity int) *Pool {
p := &Pool{
workers: make([]*Worker, numWorkers),
jobs: make(chan int, jobCapacity),
}
var wg sync.WaitGroup
wg.Add(numWorkers)
for i := 0; i < numWorkers; i++ {
p.workers[i] = &Worker{
id: i,
jobs: p.jobs,
wg: &wg,
}
go p.workers[i].Start()
}
go func() {
wg.Wait()
close(p.jobs)
}()
return p
}
// AddJob 向池中添加任务
func (p *Pool) AddJob(job int) {
p.jobs <- job
}
// Close 关闭池
func (p *Pool) Close() {
close(p.jobs)
for _, w := range p.workers {
w.wg.Wait()
}
}
- 限制并发数量:
- 原理:使用
sync.WaitGroup
和计数器来控制同时运行的goroutine数量。例如在进行大量文件下载任务时,限制并发下载的数量,防止过多的下载任务占用过多网络和系统资源。
- 示例代码:
package main
import (
"fmt"
"sync"
)
func main() {
var wg sync.WaitGroup
maxConcurrent := 3
semaphore := make(chan struct{}, maxConcurrent)
tasks := []int{1, 2, 3, 4, 5, 6}
for _, task := range tasks {
semaphore <- struct{}{}
wg.Add(1)
go func(t int) {
defer func() {
<-semaphore
wg.Done()
}()
fmt.Printf("Processing task %d\n", t)
}(task)
}
wg.Wait()
}
- 合理设置缓冲区:
- 原理:在通道(channel)上设置合理的缓冲区大小。如果缓冲区过小,可能导致goroutine频繁阻塞等待;如果缓冲区过大,可能会占用过多内存。例如在生产者 - 消费者模型中,合理设置缓冲区大小可以平衡数据生产和消费的速度,避免资源浪费。
- 示例代码:
package main
import (
"fmt"
)
func producer(ch chan<- int) {
for i := 0; i < 10; i++ {
ch <- i
}
close(ch)
}
func consumer(ch <-chan int) {
for num := range ch {
fmt.Printf("Consumed %d\n", num)
}
}
func main() {
// 设置合理的缓冲区大小,例如10
ch := make(chan int, 10)
go producer(ch)
consumer(ch)
}
- 及时释放资源:
- 原理:确保在goroutine结束时,及时释放其占用的资源,如文件句柄、数据库连接等。例如在使用数据库连接时,使用
defer
语句关闭连接,避免资源泄漏。
- 示例代码:
package main
import (
"database/sql"
"fmt"
_ "github.com/lib/pq" // 假设使用PostgreSQL
)
func main() {
db, err := sql.Open("postgres", "user=test dbname=test sslmode=disable")
if err != nil {
fmt.Println("Failed to connect to database:", err)
return
}
defer db.Close()
// 在这里执行数据库操作
var result string
err = db.QueryRow("SELECT 'test'").Scan(&result)
if err != nil {
fmt.Println("Query error:", err)
return
}
fmt.Println("Result:", result)
}
实际场景
- Web爬虫:
- 场景描述:爬取大量网页信息时,需要启动多个goroutine并发请求网页。但如果不控制goroutine数量,可能会对目标服务器造成过大压力,同时也会耗尽本地资源。
- 代码示例:
package main
import (
"fmt"
"net/http"
"sync"
)
func fetchURL(url string, wg *sync.WaitGroup, semaphore chan struct{}) {
defer func() {
<-semaphore
wg.Done()
}()
resp, err := http.Get(url)
if err != nil {
fmt.Printf("Error fetching %s: %v\n", url, err)
return
}
defer resp.Body.Close()
fmt.Printf("Fetched %s successfully\n", url)
}
func main() {
urls := []string{
"https://example.com",
"https://google.com",
"https://github.com",
}
var wg sync.WaitGroup
maxConcurrent := 2
semaphore := make(chan struct{}, maxConcurrent)
for _, url := range urls {
semaphore <- struct{}{}
wg.Add(1)
go fetchURL(url, &wg, semaphore)
}
wg.Wait()
}
- 数据处理流水线:
- 场景描述:在对大量数据进行复杂处理时,可能会将处理过程分为多个阶段,每个阶段由不同的goroutine负责。例如,先从文件读取数据,然后进行数据清洗,最后进行数据分析。合理管理各个阶段的goroutine数量和通道缓冲区大小,可以提高处理效率并有效利用资源。
- 示例代码:
package main
import (
"fmt"
)
func readData(ch chan<- int) {
for i := 1; i <= 10; i++ {
ch <- i
}
close(ch)
}
func cleanData(in <-chan int, out chan<- int) {
for num := range in {
if num%2 == 0 {
out <- num
}
}
close(out)
}
func analyzeData(in <-chan int) {
for num := range in {
fmt.Printf("Analyzed clean data: %d\n", num)
}
}
func main() {
dataCh := make(chan int)
cleanCh := make(chan int)
go readData(dataCh)
go cleanData(dataCh, cleanCh)
analyzeData(cleanCh)
}