首页
技术小册
AIGC
面试刷题
技术文章
MAGENTO
云计算
视频课程
源码下载
PDF书籍
「涨薪秘籍」
登录
注册
01 | Mutex:如何解决资源并发访问问题?
02 | Mutex:庖丁解牛看实现
03|Mutex:4种易错场景大盘点
04| Mutex:骇客编程,如何拓展额外功能?
05| RWMutex:读写锁的实现原理及避坑指南
06 | WaitGroup:协同等待,任务编排利器
07 | Cond:条件变量的实现机制及避坑指南
08 | Once:一个简约而不简单的并发原语
09 | map:如何实现线程安全的map类型?
10 | Pool:性能提升大杀器
11 | Context:信息穿透上下文
12 | atomic:要保证原子操作,一定要使用这几种方法
13 | Channel:另辟蹊径,解决并发问题
14 | Channel:透过代码看典型的应用模式
15 | 内存模型:Go如何保证并发读写的顺序?
16 | Semaphore:一篇文章搞懂信号量
17 | SingleFlight 和 CyclicBarrier:请求合并和循环栅栏该怎么用?
18 | 分组操作:处理一组子任务,该用什么并发原语?
19 | 在分布式环境中,Leader选举、互斥锁和读写锁该如何实现?
20 | 在分布式环境中,队列、栅栏和STM该如何实现?
当前位置:
首页>>
技术小册>>
Golang并发编程实战
小册名称:Golang并发编程实战
### 16 | Semaphore:一篇文章搞懂信号量 在并发编程的广阔天地中,信号量(Semaphore)是一种重要的同步机制,它用于控制对共享资源的访问数量,确保资源的有效利用和程序的正确执行。在Go语言(Golang)中,虽然标准库中没有直接提供信号量的实现,但我们可以借助`sync`包中的`WaitGroup`或其他机制,以及通过`golang.org/x/sync/semaphore`包(自Go 1.18起,该包已成为官方推荐方式)来实现信号量的功能。本章将深入探讨信号量的概念、原理、应用场景以及在Go语言中的实现方式。 #### 一、信号量的基本概念 信号量(Semaphore)是一种用于控制多个进程或线程对共享资源访问的计数器。它通常用于实现生产者-消费者问题、读写锁、资源池管理等场景。信号量的基本操作包括两种: 1. **等待(Wait/P操作)**:当线程尝试访问共享资源时,它会执行等待操作。如果信号量的值大于0,表示还有可用的资源,信号量的值减1,线程继续执行。如果信号量的值为0,表示所有资源都被占用,线程将被阻塞,直到有资源被释放。 2. **信号(Signal/V操作)**:当线程完成对共享资源的访问后,它会执行信号操作,将信号量的值加1,以表示释放了一个资源。如果有线程在等待该资源(即信号量的值小于等待线程的数量),那么其中一个等待的线程将被唤醒并继续执行。 信号量的这些操作保证了资源的正确分配和回收,避免了资源竞争和死锁等问题。 #### 二、信号量的原理 信号量的实现原理基于操作系统的进程同步机制,特别是互斥锁(Mutex)和条件变量(Condition Variable)。在Go语言中,虽然没有直接提供信号量的API,但可以通过`sync`包中的工具,如`Mutex`和`Cond`,或者利用`golang.org/x/sync/semaphore`包来模拟信号量的行为。 `golang.org/x/sync/semaphore`包提供了一个简洁的API来创建和操作信号量。它内部使用了一种高效的算法来管理等待的goroutine,避免了传统条件变量可能导致的上下文切换和性能开销。 #### 三、信号量的应用场景 1. **生产者-消费者问题**:在生产者-消费者模型中,生产者生产数据放入缓冲区,消费者从缓冲区取出数据处理。信号量可以用来控制缓冲区的容量,防止生产者过快地填充缓冲区而溢出,或消费者过慢地消费数据导致缓冲区空置。 2. **资源池管理**:在需要管理有限资源(如数据库连接、线程池等)的场景中,信号量可以用来控制同时访问资源的数量,确保资源不被过度使用。 3. **并发控制**:在复杂的并发系统中,信号量可以用来限制特定操作的并发执行数量,以优化系统性能和资源利用率。 #### 四、Go语言中的信号量实现 从Go 1.18开始,`golang.org/x/sync/semaphore`包成为官方推荐的信号量实现方式。以下是如何在Go语言中使用该包来创建一个简单的信号量,并控制对共享资源的访问。 ##### 4.1 安装`golang.org/x/sync/semaphore` 首先,确保你的项目中包含了`golang.org/x/sync/semaphore`包。如果尚未安装,可以通过以下命令安装: ```bash go get golang.org/x/sync/semaphore ``` ##### 4.2 使用信号量 下面是一个使用信号量控制并发访问共享资源的简单示例: ```go package main import ( "context" "fmt" "golang.org/x/sync/semaphore" "sync" "time" ) // 假设我们有一个需要并发访问的共享资源 var resource = make(chan struct{}, 1) // 简单的资源池,仅用于演示 func worker(id int, sem *semaphore.Weighted, wg *sync.WaitGroup) { defer wg.Done() // 等待获取信号量 if err := sem.Acquire(context.Background(), 1); err != nil { fmt.Printf("worker %d failed to acquire semaphore: %v\n", id, err) return } defer sem.Release(1) // 模拟访问共享资源 resource <- struct{}{} // 假设这是访问资源的操作 time.Sleep(time.Second) // 模拟处理时间 <-resource // 释放资源 fmt.Printf("worker %d processed\n", id) } func main() { var wg sync.WaitGroup sem := semaphore.NewWeighted(5) // 创建一个权重为5的信号量 // 启动10个工作goroutine for i := 0; i < 10; i++ { wg.Add(1) go worker(i, sem, &wg) } wg.Wait() // 等待所有工作goroutine完成 } ``` 在上面的示例中,我们创建了一个权重为5的信号量,表示同时最多有5个goroutine可以访问共享资源。我们启动了10个工作goroutine,每个goroutine在访问共享资源前都需要先通过`sem.Acquire`方法获取信号量。如果信号量的当前值小于请求的权重,`Acquire`方法将阻塞调用者,直到有足够的信号量可用。访问完资源后,通过`sem.Release`方法释放信号量。 #### 五、总结 信号量作为并发编程中重要的同步机制,在控制资源访问、优化系统性能等方面发挥着重要作用。在Go语言中,虽然标准库没有直接提供信号量的实现,但通过`golang.org/x/sync/semaphore`包,我们可以方便地在Go程序中实现和使用信号量。通过深入理解信号量的概念、原理和应用场景,我们可以更好地利用这一工具来构建高效、可靠的并发系统。
上一篇:
15 | 内存模型:Go如何保证并发读写的顺序?
下一篇:
17 | SingleFlight 和 CyclicBarrier:请求合并和循环栅栏该怎么用?
该分类下的相关小册推荐:
Golang修炼指南
Go语言入门实战经典
深入浅出Go语言核心编程(七)
从零写一个基于go语言的Web框架
深入浅出Go语言核心编程(三)
深入浅出Go语言核心编程(四)
Go语言从入门到实战
深入浅出Go语言核心编程(八)
Go Web编程(中)
Go开发权威指南(上)
go编程权威指南(一)
深入浅出Go语言核心编程(五)