簡介
信号量是并發程式設計中常見的一種同步機制,在需要控制通路資源的線程數量時就會用到信号量
使用場景
在需要控制通路資源的線程數量時就會需要信号量
我來舉個例子幫助你了解。假設我們有一組要抓取的頁面,資源有限最多允許我們同時執行三個抓取任務,當同時有三個抓取任務在執行時,在執行完一個抓取任務後才能執行下一個排隊等待的任務。當然這個問題用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申請較多的資源,由于上面說的歸還後喚醒等待者的政策,它可能會等待比較長的時間。
參考:
Go 并發程式設計-信号量的使用方法和其實作原理