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. 其他
根据以上思路, 可以扩展更多的方法, 比如为每个用户单独生成一个并发执行的"线程池"