天天看點

信号量的使用方法簡介使用場景注意問題

簡介

信号量是并發程式設計中常見的一種同步機制,在需要控制通路資源的線程數量時就會用到信号量

使用場景

在需要控制通路資源的線程數量時就會需要信号量

我來舉個例子幫助你了解。假設我們有一組要抓取的頁面,資源有限最多允許我們同時執行三個抓取任務,當同時有三個抓取任務在執行時,在執行完一個抓取任務後才能執行下一個排隊等待的任務。當然這個問題用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 并發程式設計-信号量的使用方法和其實作原理