golang 函数化并发同步执行

169 阅读1分钟

1. 总体思路

首先需要一个支持并发和同步的信号量工具类, 再基于这个工具类封装需要同步并发执行的方法, 最后根据具体业务需求做进一步封装

2. 信号量工具类

这个工具类可以基于golang的channel或者分布并发锁或者其他方式, 只要支持同步和并发就可以. 我们使用channel, 并且网上有一段开源代码可以使用. 如下


type Semaphore struct {
	permits int      // 许可数量
	channel chan int // 通道
}

/* 创建信号量 */
func NewSemaphore(permits int) *Semaphore {
	return &Semaphore{channel: make(chan int, permits), permits: permits}
}

/* 获取许可 */
func (s *Semaphore) Acquire() {
	s.channel <- 0
}

/* 释放许可 */
func (s *Semaphore) Release() {
	<-s.channel
}

/* 尝试获取许可 */
func (s *Semaphore) TryAcquire() bool {
	select {
	case s.channel <- 0:
		return true
	default:
		return false
	}
}

/* 尝试指定时间内获取许可 */
func (s *Semaphore) TryAcquireOnTime(timeout time.Duration) bool {
	for {
		select {
		case s.channel <- 0:
			return true
		case <-time.After(timeout):
			return false
		}
	}
}

/* 当前可用的许可数 */
func (s *Semaphore) AvailablePermits() int {
	return s.permits - len(s.channel)
}

3. 并发执行方法的函数

可以用java中的线程池来类比,parallelMax为可同时并发执行的数量

func ParallelFunc(parallelMax int) func(f func() (interface{}, error)) (interface{}, error) {
	var sema = NewSemaphore(parallelMax)
	return func(f func() (interface{}, error)) (interface{}, error) {
		defer func() {
			sema.Release()
		}()
		sema.Acquire()
		ret, err := f()
		return ret, err
	}
}

再封装一个可并发1个的方法, 这样可以顺序执行一个方法的多次调用

func SyncFunc() func(f func() (interface{}, error)) (interface{}, error) {
	return ParallelFunc(1)
}

4. 使用

var syncFunc = SyncFunc()

syncFunc(func() (interface{}, error) {
	// do something
	return nil, nil
})

5. 其他

根据以上思路, 可以扩展更多的方法, 比如为每个用户单独生成一个并发执行的"线程池"