zl程序教程

您现在的位置是:首页 >  其他

当前栏目

Golang实现协程池

2023-02-18 16:32:40 时间

通过无缓冲的通道实现Worker池,无缓冲的通道好处是:1. 任务不会丢失,所有投递的任务都一定会被处理,如果协程池里的协程都在忙碌中的话,那么会阻塞在往通道投递任务的那一行代码。2. 调用者可以及时的知道协程池是否处于忙碌的状态中。

以下是Work包中的代码:

package work

import "sync"

// work包的目的是展示如何使用无缓冲的通道来创建一个goroutine池, 这些goroutine执行并控制一组工作
// 让其并发工作。在这种情况下, 使用无缓冲的通道要比随意指定一个缓冲区大小的有缓冲的通道好,
// 因为这种情况既不需要一个工作队列,也不需要一组goroutine配合执行。
// 无缓冲的通道保证两个goroutine之间的数据交换。这种使用无缓冲的通道的方法允许调用者知道
// 什么时候goroutine池正在执行工作, 而且如果池中的所有goroutine都在忙,也可以及时通过通道通知调用者。
// 使用无缓冲的通道不会有工作在队列中丢失或者卡住,所有工作都会被处理。

// Worker必须满足接口类型
// 才能使用工作池
type Worker interface {
	Task()
}

// Pool提供一个goroutine池, 这个池可以完成任何已提交的Worker任务
type Pool struct {
	work 		chan Worker
	wg 			sync.WaitGroup
}

// New创建一个新协程池
func New(maxGoroutines int) *Pool {
	p := Pool{
		work: make(chan Worker),
	}
	p.wg.Add(maxGoroutines)
	for i := 0; i < maxGoroutines; i++ {
		go func() {
			// p.work关闭的时候将该协程从waitgroup中关闭
			defer p.wg.Done()
			for w := range p.work {
				// 阻塞等待执行任务
				w.Task()
			}
		}()
	}
	return &p
}

// Run提交工作到协程池
func (p *Pool) Run(w Worker) {
	p.work <- w
}

// Shutdown 等待所有goroutine停止工作
func (p *Pool) Shutdown() {
	close(p.work)
	p.wg.Wait()
}

让我们一起通过main包中的代码来看看如何使用协程池:

package main

import (
	"GoPratice/work"
	"log"
	"sync"
	"time"
)

// 通过main.go调用work包中的协程池
// 这个示例程序展示如何使用work包
// 创建一个goroutine池并完成工作

//  names 提供一组用来显示的名字
var names = []string{
	"steve",
	"bob",
	"mary",
	"therese",
	"jason",
}

// namePrinter 使用特定方式打印名字
type namePrinter struct {
	name string
}

// Task实现Worker接口
func (m *namePrinter) Task() {
	log.Println(m.name)
	time.Sleep(time.Second)
}

func init() {
	log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)
}

func main() {
	// 使用两个goroutine来创建协程池
	p := work.New(2)
	var wg sync.WaitGroup
	wg.Add(100 * len(names))

	for i := 0; i < 100; i++ {
		// 迭代names切片
		for _, name := range names {
			// 创建一个namePrinter并提供
			// 指定的名字
			np := namePrinter{
				name: name,
			}
			go func() {
				p.Run(&np)
				wg.Done()
			}()
		}
	}

	wg.Wait()

	// 让工作池停止工作, 等待所有的工作完成
	p.Shutdown()
}