简介
信号量是并发编程中常见的一种同步机制,在需要控制访问资源的线程数量时就会用到信号量
使用场景
在需要控制访问资源的线程数量时就会需要信号量
我来举个例子帮助你理解。假设我们有一组要抓取的页面,资源有限最多允许我们同时执行三个抓取任务,当同时有三个抓取任务在执行时,在执行完一个抓取任务后才能执行下一个排队等待的任务。当然这个问题用Channel也能解决,不过这次我们使用Go提供的信号量原语来解决这个问题,代码如下:
package main
import (
"context"
"fmt"
"sync"
"time"
"golang.org/x/sync/semaphore"
)
func doSomething(u string) {
// 模拟抓取任务的执行
fmt.Println(u)
time.Sleep(2 * time.Second)
}
const (
Limit = 3 // 同時并行运行的goroutine上限
Weight = 1 // 每个goroutine获取信号量资源的权重
)
func main() {
urls := []string{
"http://www.example.com",
"http://www.example.net",
"http://www.example.net/foo",
"http://www.example.net/bar",
"http://www.example.net/baz",
}
s := semaphore.NewWeighted(Limit)
var w sync.WaitGroup
for _, u := range urls {
w.Add(1)
go func(u string) {
s.Acquire(context.Background(), Weight)
doSomething(u)
s.Release(Weight)
w.Done()
}(u)
}
w.Wait()
fmt.Println("All Done")
}
注意问题
- Acquire和
TryAcquire方法都可以用于获取资源,前者会阻塞地获取信号量。后者会非阻塞地获取信号量,如果获取不到就返回false。 - Release归还信号量后,会以先进先出的顺序唤醒等待队列中的调用者。如果现有资源不够处于等待队列前面的调用者请求的资源数,所有等待者会继续等待。
- 如果一个goroutine申请较多的资源,由于上面说的归还后唤醒等待者的策略,它可能会等待比较长的时间。