信号量的使用方法

简介

信号量是并发编程中常见的一种同步机制,在需要控制访问资源的线程数量时就会用到信号量

使用场景

在需要控制访问资源的线程数量时就会需要信号量

我来举个例子帮助你理解。假设我们有一组要抓取的页面,资源有限最多允许我们同时执行三个抓取任务,当同时有三个抓取任务在执行时,在执行完一个抓取任务后才能执行下一个排队等待的任务。当然这个问题用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 并发编程-信号量的使用方法和其实现原理

猜你喜欢

转载自blog.csdn.net/csdniter/article/details/113102889