在go中使用chan实现一个简易信号量机制,进而实现并发数量的控制

信号量工具类

信号量是操作系统中一个经典的进程同步机制,它可以通过阻塞和唤醒进程,来实现进程间的同步、并发数量的限制、互斥资源的访问等等。

go语言中的chan也具有阻塞和唤醒协程的功能,所以我就使用chan实现了一个简易的信号量机制。我所实现的主要是信号量的P、V操作:

  • P操作是申请并发资源,如果有资源则继续执行,如果没有资源则阻塞。
  • V操作是释放并发资源,如果资源池已满则阻塞,否则释放并发资源,并唤醒一个被阻塞的协程。

其实阻塞和唤醒这个功能不需要我来写,chan自带这个功能,我只是基于chan做进一步的封装,组装一些更便于使用的接口罢了。

具体代码如下:
/util/semaphore.go

package util

import "errors"

type semaphore struct {
	c         chan int8
	initCount int
	maxCount  int
}

// NewSemaphore initCount是信号量的初始值
// P操作请求资源,当信号量的值小于等于0时,再执行P操作会阻塞
// V操作释放资源,当信号量的值等于maxCount时,再执行V操作会阻塞
func NewSemaphore(initCount int, maxCount int) (*semaphore, error) {
	if maxCount <= 0 || initCount < 0 || initCount > maxCount {
		return nil, errors.New("参数错误")
	}
	s := &semaphore{
		c: make(chan int8, maxCount),
	}
	for i := 1; i <= initCount; i++ {
		s.c <- 1
	}
	return s, nil
}

// P P操作
func (s *semaphore) P() {
	_ = <-s.c
}

// V V操作
func (s *semaphore) V() {
	s.c <- 1
}

// NowCount 获得当前时刻信号量的值
func (s *semaphore) NowCount() int {
	return len(s.c)
}

// MaxCount 获得信号量的maxCount
func (s *semaphore) MaxCount() int {
	return s.maxCount
}

// InitCount 获得信号量的initCount
func (s *semaphore) InitCount() int {
	return s.initCount
}

// Close 关闭信号量
func (s *semaphore) Close() {
	close(s.c)
}

测试代码

在测试代码中我利用上述功能,模拟了并发数量限制的效果。

具体来说就是有n个协程,每个协程中会在控制台输出一个数字,我使用信号量对并发执行数量进行限制,使最多有nLimit个协程同时执行。

具体代码如下:
/test/01_test.go

package test

import (
	"fmt"
	"smCrawler/util"
	"testing"
	"time"
)

// 通过限制输出数字的频率,来模拟n个进程中最多同时执行nLimit个进程的并发限制
func Test001(t *testing.T) {
	// 数的总个数和每秒钟最多输出的数的个数
	n, nLimit := 30, 5
	// 限制数字输出频率的信号量
	numS, _ := util.NewSemaphore(nLimit, nLimit)
	// 程序运行结束的信号量
	overS, _ := util.NewSemaphore(1, 1)
	// 计数器,以输出数字的个数
	counter := 0

	overS.P()

	for i := 1; i <= n; i++ {
		num := i //会有闭包现象,在go func执行的时候,此作用域的i值已经递增过了,所以要用中间变量读取一下i值
		go func() {
			numS.P()
			defer numS.V()

			fmt.Println(num)
			counter++
			if counter == n {
				overS.V()
			}

			time.Sleep(time.Second)
		}()
	}
	overS.P()
	overS.Close()
	numS.Close()
	fmt.Println("over.......")
}

运行结果如下图所示,当nLimit为5的时候,每次控制台输出5个数字

在go中使用chan实现一个简易信号量机制,进而实现并发数量的控制_第1张图片

你可能感兴趣的:(golang,开发语言,后端)