go 协程池的实现

使用场景

这次需求是做一个临时的数据采集功能,为了将积压的数据快速的消耗完,但是单一的脚本消耗的太慢,于是乎就手写了一个简单的协程池:

  1. 为了能加快数据的收集速度
  2. 为了稳定协程的数量,让脚本变得稳定

设计图如下

在这里插入图片描述
协程池中提供了三个方法:

  1. 一个是Addjob用来将任务加入到任务池中
  2. Do 是用来消耗任务池中的任务
  3. HandleErrors 用来获取到错误信息
  4. Stop 是当脚本停止以后,不会立刻停止而是等待所有的人物消耗光在停止

代码如下

该协程池是借用了go扩展库中的semaphore来实现的。

  1. semaphore 信号量是一种同步机制,用于控制对共享资源的访问,常用于限制可以同时访问某一资源或资源池的线程数量。
  2. 我使用的是Acquire函数来实现的,Acquire 当资源访问量达到上限时会被阻塞,直到有协程执行完成,所以我们这里需要对Acquire的上下文设置超时时间,防止我们的任务出现死任务无法退出,从而导致整个协程池堵死。
  3. 我们在任务执行完成后要通过Release来释放资源,防止我们池子越变越小。
package pool

import (
	"context"
	"sync"
	"time"

	"golang.org/x/sync/semaphore"
)

type GoPool struct {
	MaxNum int
	Jobs   chan func() error
	sem    *semaphore.Weighted
	wg     *sync.WaitGroup
	Errs   chan error
}

func NewGoPool(num int) *GoPool {
	return &GoPool{
		MaxNum: num,
		Jobs:   make(chan func() error, num),
		sem:    semaphore.NewWeighted(int64(num)),
		wg:     &sync.WaitGroup{},
		Errs:   make(chan error, num),
	}
}

func (g *GoPool) Do() {

	go g.gAcquire()
}

func (g *GoPool) AddJob(f func() error) {
	g.Jobs <- f
}

func (g *GoPool) gAcquire() {

	for {
		ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
		select {
		case job, ok := <-g.Jobs:
			if !ok {
				cancel()
				return
			}
			g.wg.Add(1)
			if err := g.sem.Acquire(ctx, 1); err != nil {
				// g.Errs <- err
				g.wg.Done()
				cancel() // 确保在退出前取消context
				break
			}
			go func() {
				defer g.sem.Release(1)
				defer g.wg.Done()
				if err := job(); err != nil {
					g.Errs <- err
					return
				}
			}()
		case <-ctx.Done():
			return
		default:
			continue
		}
	}
}

func (g *GoPool) Stop() {
	close(g.Jobs)
	g.wg.Wait()
	close(g.Errs)
}


func (g *GoPool) HandleErrors(handler func(error)) {
	for err := range g.Errs {
		handler(err)
	}
}

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
协程是一种将协程资源进行复用的技术,可以有效地提高协程的利用效率和整个应用的性能。在PHP中,可以使用Swoole扩展提供的协程实现。 Swoole提供了一个Swoole\Coroutine\Channel类,该类可以用于实现协程。具体实现过程如下: 1. 创建一个Swoole\Coroutine\Channel对象,用于存储协程资源; 2. 调用Swoole\Coroutine\run()方法创建一个协程,其中每个协程都会从Swoole\Coroutine\Channel对象中获取协程资源,并执行相应的任务; 3. 将任务添加到协程中的协程中,等待协程执行完毕后,将协程资源返回给Swoole\Coroutine\Channel对象,以便下次使用。 下面是一个简单的示例代码: ```php $pool = new Swoole\Coroutine\Channel(10); Swoole\Coroutine\run(function () use ($pool) { for ($i = 0; $i < 10; $i++) { go(function () use ($i, $pool) { // 从协程中获取协程资源 $resource = $pool->pop(); // 执行任务 echo "Coroutine {$i} started\n"; co::sleep(1); echo "Coroutine {$i} finished\n"; // 将协程资源返回给协程 $pool->push($resource); }); } }); ``` 在上面的代码中,我们首先创建了一个Swoole\Coroutine\Channel对象,用于存储协程资源。然后,我们创建了一个包含10个协程协程,并将每个协程添加到中。每个协程都会从协程中获取一个协程资源,并执行相应的任务。任务执行完毕后,将协程资源返回给协程,以便下次使用。 需要注意的是,协程的大小应该根据实际情况进行设置。如果的大小太小,可能会导致协程资源不足;如果的大小太大,可能会导致协程资源的浪费。在实际使用中,可以根据负载情况动态地调整协程的大小。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值