Go语言中的信号量:原理与实践指南

news2025/2/26 15:04:47

Go语言中的信号量:原理与实践指南

引言

在并发编程中,控制对共享资源的访问是一个经典问题。Go语言提供了丰富的并发原语(如sync.Mutex),但当我们需要灵活限制并发数量时,信号量(Semaphore)便成为重要工具。本文将深入解析Go中信号量的实现方式,并通过代码示例演示其典型应用场景。


一、信号量基础

什么是信号量?

信号量是一种同步机制,用于限制同时访问某资源的线程(或goroutine)数量。其核心是一个计数器,操作包括:

  • P操作(获取):计数器减1,若计数器为0则阻塞等待
  • V操作(释放):计数器加1,唤醒等待的线程

与互斥锁(Mutex)的区别:

特性互斥锁信号量
并发限制数量1可自定义(N≥1)
适用场景严格互斥访问流量控制、资源池

二、Go中的两种实现方案

方案1:基于Channel的实现(标准库方式)

go
package main

import (
"fmt"
"sync"
"time"
)

func main() {
const maxConcurrent = 2 // 最大并发数
sem := make(chan struct{}, maxConcurrent)
var wg sync.WaitGroup

    for i := 1; i <= 5; i++ {
        wg.Add(1)
        go func(id int) {
            defer wg.Done()
            
            sem <- struct{}{}         // 获取信号量
            defer func() { <-sem }()  // 释放信号量
            
            fmt.Printf("Worker %d started\n", id)
            time.Sleep(time.Second)   // 模拟工作负载
            fmt.Printf("Worker %d done\n", id)
        }(i)
    }

    wg.Wait()
    fmt.Println("All workers completed")
}

代码解析

  1. sem := make(chan struct{}, N) 创建容量为N的缓冲通道
  2. sem <- struct{}{} 通过发送空结构体占用槽位
  3. <-sem 接收数据释放槽位
  4. defer确保无论流程如何都会释放资源

方案2:使用semaphore.Weighted(扩展库实现)

bash
go get golang.org/x/sync/semaphore  # 安装依赖
go
package main

import (
"context"
"fmt"
"golang.org/x/sync/semaphore"
"sync"
"time"
)

func main() {
const (
maxConcurrent = 2    // 最大并发数
totalWorkers  = 5    // 总任务数
)

    sem := semaphore.NewWeighted(maxConcurrent)
    ctx := context.Background()
    var wg sync.WaitGroup

    for i := 1; i <= totalWorkers; i++ {
        wg.Add(1)
        go func(id int) {
            defer wg.Done()

            // 尝试获取信号量
            if err := sem.Acquire(ctx, 1); err != nil {
                fmt.Printf("Worker %d failed: %v\n", id, err)
                return
            }
            defer sem.Release(1)

            fmt.Printf("Worker %d started\n", id)
            time.Sleep(time.Second)
            fmt.Printf("Worker %d done\n", id)
        }(i)
    }

    wg.Wait()
    fmt.Println("All workers completed")
}

特性说明

  • 支持加权请求(如一次申请多个许可)
  • 可结合context.Context实现超时控制
  • 更适用于复杂资源管理场景

三、关键应用场景

1. 数据库连接池控制

go
// 创建最大10连接的信号量
var dbSem = semaphore.NewWeighted(10)

func QueryDatabase(query string) {
dbSem.Acquire(context.Background(), 1)
defer dbSem.Release(1)

    // 执行数据库操作
}

2. 限流下载器

go
// 限制同时下载数为3
var downloadSem = make(chan struct{}, 3)

func DownloadFile(url string) {
downloadSem <- struct{}{}
defer func() { <-downloadSem }()

    // 执行下载逻辑
}

3. 批量任务分流

go
// 控制100个并发处理任务
sem := semaphore.NewWeighted(100)
for _, task := range tasks {
go func(t Task) {
sem.Acquire(ctx, 1)
defer sem.Release(1)
process(t)
}(task)
}

四、实现方案对比

维度Channel实现semaphore.Weighted
标准库支持✅ 无需额外依赖❌ 需要安装扩展库
加权请求❌ 不支持✅ 支持
超时控制需搭配select实现✅ 原生支持Context
易用性简单场景推荐复杂场景推荐
性能开销较低略高(含锁机制)

五、最佳实践建议

  1. 资源释放
    始终使用defer释放信号量,避免协程异常导致资源泄漏

  2. 容量规划
    根据实际硬件资源(CPU核心数、IO带宽等)设置合理并发数

  3. 异常处理
    使用semaphore.Weighted时检查Acquire()返回的error

  4. 调试技巧
    添加指标监控当前信号量使用率:

go
fmt.Printf(“Available: %d/%d\n”, len(sem), cap(sem))

结语

信号量为Go并发编程提供了灵活的资源管控能力。无论是简单的通道实现,还是功能更强的semaphore.Weighted,开发者都可以根据具体需求选择合适的方案。合理使用信号量不仅能提升程序稳定性,还能有效避免资源竞争导致的性能瓶颈。

扩展阅读

  • Go官方并发指南
  • semaphore包源码分析

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2306425.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ReentrantLock 用法与源码剖析笔记

&#x1f4d2; ReentrantLock 用法与源码剖析笔记 &#x1f680; 一、ReentrantLock 核心特性 &#x1f504; 可重入性&#xff1a;同一线程可重复获取锁&#xff08;最大递归次数为 Integer.MAX_VALUE&#xff09;&#x1f527; 公平性&#xff1a;支持公平锁&#xff08;按等…

java进阶专栏的学习指南

学习指南 java类和对象java内部类和常用类javaIO流 java类和对象 类和对象 java内部类和常用类 java内部类精讲Object类包装类的认识String类、BigDecimal类初探Date类、Calendar类、SimpleDateFormat类的认识java Random类、File类、System类初识 javaIO流 java IO流【…

架构思维:架构的演进之路

文章目录 引言为什么架构思维如此重要架构师的特点软件架构的知识体系如何提升架构思维大型互联网系统架构的演进之路一、大型互联网系统的特点二、系统处理能力提升的两种途径三、大型互联网系统架构演化过程四、总结 引言 在软件开发行业中&#xff0c;有很多技术人可能会问…

vue3:vue3项目安装并引入Element-plus

一、安装Element-plus 1、安装语句位置 安装 | Element Plushttps://element-plus.org/zh-CN/guide/installation.html根据所需进行安装&#xff0c;这里使用npm包 2、找到项目位置 找到项目位置&#xff0c;在路径上输入cmd回车打开“运行”窗口 输入安装语句回车完成安装 …

java.2.25

1. 注释 ​ 注释是对代码的解释和说明文字。 Java中的注释分为三种&#xff1a; 单行注释&#xff1a; // 这是单行注释文字多行注释&#xff1a; /* 这是多行注释文字 这是多行注释文字 这是多行注释文字 */ 注意&#xff1a;多行注释不能嵌套使用。文档注释&#xff1a;…

VScode 开发

目录 安装 VS Code 创建一个 Python 代码文件 安装 VS Code VSCode&#xff08;全称&#xff1a;Visual Studio Code&#xff09;是一款由微软开发且跨平台的免费源代码编辑器&#xff0c;VSCode 开发环境非常简单易用。 VSCode 安装也很简单&#xff0c;打开官网 Visual S…

A Large Recurrent Action Model: xLSTM Enables Fast Inference for Robotics Tasks

奥地利林茨约翰开普勒大学机器学习研究所 ELLIS 小组&#xff0c;LIT 人工智能实验室奥地利林茨 NXAI 有限公司谷歌 DeepMind米拉 - 魁北克人工智能研究所 摘要 近年来&#xff0c;强化学习&#xff08;Reinforcement Learning, RL&#xff09;领域出现了一种趋势&#xff0c;…

计算机毕业设计SpringBoot+Vue.js学科竞赛管理系统(源码+文档+PPT+讲解)

温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 作者简介&#xff1a;Java领…

Deep Seek-编码器

1. DeepSeek Coder 简介 DeepSeek Coder 由一系列代码语言模型组成,每个模型都在 2T 令牌上从头开始训练,其中 87% 的代码和 13% 的自然语言在中英文中组成。我们提供各种大小的代码模型,从 1B 到 33B 版本。每个模型都通过采用 16K 的窗口大小和额外的填空任务在项目级代码…

Android平台轻量级RTSP服务模块技术对接说明

一、技术背景 随着内网无纸化办公、电子教室等应用场景对超低延迟音视频传输需求的日益增长&#xff0c;为避免用户或开发者单独部署 RTSP 或 RTMP 服务&#xff0c;大牛直播 SDK 推出了轻量级 RTSP 服务 SDK。该 SDK 能够将本地音视频数据&#xff08;如摄像头、麦克风等&…

RoCEv2 高性能传输协议与 Lossless 无损网络

目录 文章目录 目录RoCERoCEv2 v.s. IBRoCEv2 协议栈RoCEv2 需要 Lossless NetworkLossless Network 拥塞控制技术网络拥塞的原因PFC 基于优先级的流量控制PFC Unfairness &#xff08;带宽分配不公平&#xff09;的问题PFC HOL&#xff08;队头拥塞&#xff09;的问题PFC Dead…

联想 SR590 服务器 530-8i RAID 控制器更换损坏的硬盘

坏了的硬盘会自动亮黄灯。用一个空的新盘来替换&#xff0c;新盘最好不要有东西。但是有东西可能也没啥&#xff0c;因为我看 RAID 控制器里有格式化的选项 1. 从 IPMI 把服务器关机&#xff0c;电源键进入绿色闪烁状态 2. 断电&#xff0c;推开塑料滑块拉出支架&#xff0c;…

城电科技|会追日的智能花,光伏太阳花开启绿色能源新篇章

当艺术与科技相遇&#xff0c;会碰撞出怎样的火花&#xff1f;城电科技推出的光伏太阳花&#xff0c;以其独特的设计与智能化的功能&#xff0c;给出了答案。这款产品不仅具备太阳能发电的实用功能&#xff0c;更是一件充满科技属性的艺术性光伏产品&#xff0c;吸引了广泛关注…

基于YOLO11深度学习的苹果叶片病害检测识别系统【python源码+Pyqt5界面+数据集+训练代码】

《------往期经典推荐------》 一、AI应用软件开发实战专栏【链接】 项目名称项目名称1.【人脸识别与管理系统开发】2.【车牌识别与自动收费管理系统开发】3.【手势识别系统开发】4.【人脸面部活体检测系统开发】5.【图片风格快速迁移软件开发】6.【人脸表表情识别系统】7.【…

多智能体框架

多个不同的角色的Agent&#xff0c;共同完成一份复杂的工作。由一个统筹管理的智能体&#xff0c;自主规划多个智能体分别做什么&#xff0c;以及执行的顺序。 agent 应该包含的属性 执行特定任务 根据其角色和目标做出决策 能够使用工具来实现目标 与其他代理沟通和协作 保留…

C#中级教程(1)——解锁 C# 编程的调试与错误处理秘籍

一、认识错误&#xff1a;编程路上的 “绊脚石” 在 C# 编程中&#xff0c;错误大致可分为两类&#xff1a;语法错误和语义错误&#xff08;逻辑错误&#xff09;。语法错误就像是写作文时的错别字和病句&#xff0c;编译器一眼就能识别出来&#xff0c;比如变量名拼写错误、符…

Jmeter接口并发测试

Apache JMeter 是一款开源的性能测试工具&#xff0c;广泛用于接口并发测试、负载测试和压力测试。以下是使用 JMeter 进行接口并发测试的详细步骤&#xff1a; 一、准备工作 安装 JMeter 下载地址&#xff1a;Apache JMeter 官网 确保已安装 Java 环境&#xff08;JMeter 依…

MySQL-增删改查

一、Create(创建) &#x1f4d6; 语法&#xff1a; INSERT INTO table_name(value_list); 当我们使用表的时候&#xff0c;就可以使用这个语法来向表中插入元素~ 我们这边创建一个用于示范的表(Student)~ create table student( id int, name varchar(20), chinese int, math…

开源堡垒机 JumpServer 社区版实战教程:发布机的配置与Website资产配置使用

文章目录 开源堡垒机 JumpServer 社区版实战教程&#xff1a;发布机的配置与Website资产配置使用一、功能简述二、应用发布机2.1 版本要求2.2 创建应用发布机2.2.1 通过WinRM的协议进行应用发布机的创建2.2.2 通过OpenSSH的协议进行应用发布机的创建2.2.2.1 下载OpenSSH2.2.2.2…