Go 并发编程深度指南

news2025/6/9 17:59:46

Go 并发编程深度指南

Go 语言以其内置的并发原语而闻名,通过 goroutine 和 channel 提供了一种高效、安全的并发编程模型。本文将全面解析 Go 的并发机制及其实际应用。

核心概念:Goroutines 和 Channels

1. Goroutines (协程)

Go 的轻量级线程实现,开销极小:

func main() {
    // 启动一个协程
    go func() {
        fmt.Println("Hello from goroutine!")
    }()
    
    // 让主程序等待一会儿
    time.Sleep(100 * time.Millisecond)
}

2. Channels (通道)

协程间通信的主要方式:

func main() {
    // 创建无缓冲通道
    ch := make(chan string)
    
    go func() {
        time.Sleep(500 * time.Millisecond)
        ch <- "message"
    }()
    
    // 阻塞等待消息
    msg := <-ch
    fmt.Println("Received:", msg)
}

并发模式与最佳实践

1. WaitGroup 控制协程组

func processTasks(tasks []string) {
    var wg sync.WaitGroup
    
    for i, task := range tasks {
        wg.Add(1) // 增加计数
        go func(task string, id int) {
            defer wg.Done() // 结束时减少计数
            processTask(task, id)
        }(task, i)
    }
    
    wg.Wait() // 等待所有完成
    fmt.Println("All tasks completed")
}

2. Worker Pool 模式

func worker(id int, jobs <-chan int, results chan<- int) {
    for j := range jobs {
        fmt.Printf("Worker %d started job %d\n", id, j)
        time.Sleep(time.Second)
        fmt.Printf("Worker %d finished job %d\n", id, j)
        results <- j * 2
    }
}

func main() {
    jobs := make(chan int, 100)
    results := make(chan int, 100)
    
    // 启动3个worker
    for w := 1; w <= 3; w++ {
        go worker(w, jobs, results)
    }
    
    // 发送9个任务
    for j := 1; j <= 9; j++ {
        jobs <- j
    }
    close(jobs)
    
    // 接收结果
    for a := 1; a <= 9; a++ {
        <-results
    }
}

3. Select 多路复用

func main() {
    ch1 := make(chan string)
    ch2 := make(chan string)
    
    go func() {
        time.Sleep(1 * time.Second)
        ch1 <- "One"
    }()
    
    go func() {
        time.Sleep(2 * time.Second)
        ch2 <- "Two"
    }()
    
    // 同时等待两个通道
    for i := 0; i < 2; i++ {
        select {
        case msg1 := <-ch1:
            fmt.Println("Received", msg1)
        case msg2 := <-ch2:
            fmt.Println("Received", msg2)
        }
    }
}

4. Context 控制协程生命周期

func worker(ctx context.Context) {
    for {
        select {
        case <-ctx.Done():
            fmt.Println("Worker canceled")
            return
        case <-time.After(500 * time.Millisecond):
            fmt.Println("Working...")
        }
    }
}

func main() {
    ctx, cancel := context.WithCancel(context.Background())
    
    go worker(ctx)
    
    // 运行3秒后取消
    time.Sleep(3 * time.Second)
    cancel()
    
    // 给worker时间响应取消
    time.Sleep(500 * time.Millisecond)
}

5. Mutex 保护共享资源

type SafeCounter struct {
    mu sync.Mutex
    v  int
}

func (c *SafeCounter) Inc() {
    c.mu.Lock()
    defer c.mu.Unlock()
    c.v++
}

func (c *SafeCounter) Value() int {
    c.mu.Lock()
    defer c.mu.Unlock()
    return c.v
}

func main() {
    counter := SafeCounter{}
    var wg sync.WaitGroup
    
    for i := 0; i < 1000; i++ {
        wg.Add(1)
        go func() {
            defer wg.Done()
            counter.Inc()
        }()
    }
    
    wg.Wait()
    fmt.Println("Final count:", counter.Value())
}

高级并发模式

1. 扇入/扇出 (Fan-in/Fan-out)

// 生产者
func producer(done <-chan struct{}, nums ...int) <-chan int {
    out := make(chan int)
    go func() {
        defer close(out)
        for _, n := range nums {
            select {
            case out <- n:
            case <-done:
                return
            }
        }
    }()
    return out
}

// 消费者
func consumer(done <-chan struct{}, in <-chan int, id int) <-chan int {
    out := make(chan int)
    go func() {
        defer close(out)
        for n := range in {
            // 模拟处理
            result := n * n
            select {
            case out <- result:
            case <-done:
                return
            }
        }
    }()
    return out
}

// 扇入多个通道
func fanIn(done <-chan struct{}, chs ...<-chan int) <-chan int {
    var wg sync.WaitGroup
    out := make(chan int)
    
    // 定义输出函数
    output := func(c <-chan int) {
        defer wg.Done()
        for n := range c {
            select {
            case out <- n:
            case <-done:
                return
            }
        }
    }
    
    wg.Add(len(chs))
    for _, c := range chs {
        go output(c)
    }
    
    // 启动goroutine等待所有完成
    go func() {
        wg.Wait()
        close(out)
    }()
    return out
}

func main() {
    done := make(chan struct{})
    defer close(done)
    
    // 创建输入通道
    in := producer(done, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
    
    // 启动3个消费者
    c1 := consumer(done, in, 1)
    c2 := consumer(done, in, 2)
    c3 := consumer(done, in, 3)
    
    // 合并结果
    for result := range fanIn(done, c1, c2, c3) {
        fmt.Println("Result:", result)
    }
}

2. Future/Promise 模式

func futureWork(input int) <-chan int {
    result := make(chan int)
    go func() {
        // 模拟耗时操作
        time.Sleep(500 * time.Millisecond)
        result <- input * 2
        close(result)
    }()
    return result
}

func main() {
    f1 := futureWork(5)
    f2 := futureWork(10)
    
    // 并行执行后获取结果
    r1 := <-f1
    r2 := <-f2
    
    fmt.Println("Results:", r1, r2) // 10, 20
}

性能优化与陷阱规避

1. 限制并发数

func controlledWork(workers int) {
    sem := make(chan struct{}, workers)
    var wg sync.WaitGroup
    
    for i := 0; i < 100; i++ {
        wg.Add(1)
        go func(id int) {
            defer wg.Done()
            sem <- struct{}{}        // 获取信号量
            defer func() { <-sem }() // 释放信号量
            
            // 执行工作
            fmt.Printf("Worker %d starting\n", id)
            time.Sleep(time.Second)
            fmt.Printf("Worker %d done\n", id)
        }(i)
    }
    
    wg.Wait()
}

2. 通道选择与超时

func fetchData(url string, timeout time.Duration) (string, error) {
    ch := make(chan string, 1)
    go func() {
        // 模拟网络请求
        time.Sleep(500 * time.Millisecond)
        ch <- "Response from " + url
    }()
    
    select {
    case res := <-ch:
        return res, nil
    case <-time.After(timeout):
        return "", errors.New("request timed out")
    }
}

3. 避免竞态条件

// 坏: 共享变量无保护
var count int
for i := 0; i < 100; i++ {
    go func() {
        count++ // 数据竞争!
    }()
}

// 好: 使用互斥锁
var (
    mu    sync.Mutex
    count int
)
for i := 0; i < 100; i++ {
    go func() {
        mu.Lock()
        defer mu.Unlock()
        count++
    }()
}

// 更好: 使用通道通信
ch := make(chan struct{})
go func() {
    count := 0
    for range ch {
        count++
    }
}()
for i := 0; i < 100; i++ {
    ch <- struct{}{}
}

并发性能分析工具

  1. ​Race Detector​​:

    go run -race yourprogram.go
  2. ​pprof​​:

    import _ "net/http/pprof"
    
    func main() {
        go func() {
            log.Println(http.ListenAndServe("localhost:6060", nil))
        }()
        // 程序主体...
    }

    然后使用 go tool pprof http://localhost:6060/debug/pprof/profile 进行分析

  3. ​Benchmark​​:

    func BenchmarkWork(b *testing.B) {
        for i := 0; i < b.N; i++ {
            doWork()
        }
    }

Go 并发设计哲学

  1. ​不要通过共享内存来通信,而应通过通信来共享内存​
  2. ​并发不是并行​​ - 并发是设计结构,并行是执行方式
  3. ​利用组合而不是继承​​ - 通过组合小的并发原语构建复杂系统
  4. ​错误处理也是控制流​​ - 将错误作为值传递,通过通道处理

Go 的并发模型提供了强大而简单的工具集,使开发者能够构建高效、可伸缩的并发系统。通过理解 goroutine、channel 和各种同步原语的使用方法,开发者可以避免许多并发编程的常见陷阱,创建出更加稳健的系统。

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

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

相关文章

【Linux】进程的基本概念

目录 概念描述进程-PCB如何查看进程通过系统目录进行查看通过ps指令进行查看 通过系统调用获取进程的PID和PPID(进程标⽰符)通过系统调用创建子进程通过一段代码来介绍fork为什么要有子进程&#xff1f;fork为什么给子进程返回0&#xff0c;给父进程返回子进程的PIDfork函数到底…

设备驱动与文件系统:05 文件使用磁盘的实现

从文件使用磁盘的实现逻辑分享 我们现在讲第30讲&#xff0c;内容是文件使用磁盘的具体实现&#xff0c;也就是相关代码是如何编写的。上一节我们探讨了如何从字符流位置算出盘块号&#xff0c;这是文件操作磁盘的核心。而这节课&#xff0c;我们将深入研究实现这一核心功能的…

AI数据分析在体育中的应用:技术与实践

在现代体育竞技领域&#xff0c;"数据驱动"已不再是一个遥远的概念。尤其随着人工智能&#xff08;AI&#xff09;和大数据分析的不断成熟&#xff0c;从职业俱乐部到赛事直播平台&#xff0c;从运动员训练到球迷观赛体验&#xff0c;AI正以前所未有的方式渗透并改变…

zabbix 6 监控 docker 容器

zabbix 6 监控 docker 容器 1.安装zabbix_agent2 curl -s http://10.26.211.56:8080/centos7-agent2-install.sh | bash2.在zabbix server 端测试 zabbix_get -s 10.26.219.180 -k docker.infoZBX_NOTSUPPORTED: Cannot fetch data: Get "http://1.28/info": dial…

正则持续学习呀

源匹配为 (.*): (.*)$ 替换匹配为 "$1": "$2", 可将headers改为字典 参考 【爬虫军火库】如何优雅地复制请求头 - 知乎

Go基本语法——go语言中的四种变量定义方法

前言 在go语言中&#xff0c;定义一个变量有四种方式&#xff0c;本文单从语法的层面来介绍这几种方式 单变量定义方法 1.var 变量名 类型&#xff0c;不进行初始化 例如&#xff0c;定义一个变量a后为其赋值&#xff0c;并且打印其值&#xff0c;运行结果如下 //1.不进行…

27.【新型数据架构】-数据共享架构

27.【新型数据架构】-数据共享架构:降低数据获取成本,实时数据访问,保持数据新鲜度,促进数据经济发展,打破数据孤岛,标准化数据交换,增强数据安全性,完整审计追踪,合规性保障 一、数据共享架构的本质:打破壁垒的“数字立交桥” 传统企业或组织间的数据往往呈现“烟囱…

virtualbox 如何虚拟机ip固定

1、在网络管理里新建 2、配置网络 3、 进入linux系统&#xff0c;查看 查看 网卡是enp0s8, ifconfig 4、进入网卡配置文件 cd /etc/sysconfig/network-scripts如果没有enp0s8 &#xff0c;则使用mv ifcfg-enp0s3 ifcfg-enp0s8命令 配置项如下 TYPEEthernet PROXY_METHODn…

RKNN3588上部署 RTDETRV2

RT-DETR V2 是由百度研究团队在 2024年 提出的&#xff0c;是其广受好评的实时目标检测模型 RT-DETR 的重大升级版本。它继承了第一代 RT-DETR 利用 Transformer 架构实现端到端目标检测 和 卓越实时性能 的核心优势&#xff0c;并针对模型精度、训练效率和部署灵活性进行了全方…

Python----循环神经网络(BiLSTM:双向长短时记忆网络)

一、LSTM 与 BiLSTM对比 1.1、LSTM LSTM&#xff08;长短期记忆网络&#xff09; 是一种改进的循环神经网络&#xff08;RNN&#xff09;&#xff0c;专门解决传统RNN难以学习长期依赖的问题。它通过遗忘门、输入门和输出门来控制信息的流动&#xff0c;保留重要信息并丢弃无关…

Linux系统编程-DAY10(TCP操作)

一、网络模型 1、服务器/客户端模型 &#xff08;1&#xff09;C/S&#xff1a;client server &#xff08;2&#xff09;B/S&#xff1a;browser server &#xff08;3&#xff09;P2P&#xff1a;peer to peer 2、C/S与B/S区别 &#xff08;1&#xff09;客户端不同&#…

基于eclipse进行Birt报表开发

Birt报表开发最终实现效果&#xff1a; 简洁版的Birt报表开发实现效果&#xff0c;仅供参考&#xff01; 可动态获取采购单ID&#xff0c;来打印出报表&#xff01; 下面开始Birt报表开发教程&#xff1a; 首先&#xff1a;汉化的eclipse及Birt值得拥有&#xff1a;至少感觉上…

GPU虚拟化

引言 现有如下环境&#xff08;注意相关配置&#xff1a;只有一个k8s节点&#xff0c;且该节点上只有一张GPU卡&#xff09;&#xff1a; // k8s版本 $ kubectl version Client Version: version.Info{Major:"1", Minor:"22", GitVersion:"v1.22.7&…

LabVIEW工业级多任务实时测控系统

采用LabVIEW构建了一套适用于工业自动化领域的多任务实时测控系统。系统采用分布式架构&#xff0c;集成高精度数据采集、实时控制、网络通信及远程监控等功能&#xff0c;通过硬件与软件的深度协同&#xff0c;实现对工业现场多类型信号的精准测控&#xff0c;展现 LabVIEW 在…

破解HTTP无状态:基于Java的Session与Cookie协同工作指南

HTTP协议自身是属于“无状态”协议 无状态是指&#xff1a;默认情况下&#xff0c;HTTP协议的客户端和服务器之间的这次通信&#xff0c;和下次通信之间没有直接的关系 但在实际开发中&#xff0c;我们很多时候是需要知道请求之间的关联关系的 上述图中的令牌&#xff0c;通常就…

JS 事件流机制详解:冒泡、捕获与完整事件流

JS 事件流机制详解&#xff1a;冒泡、捕获与完整事件流 文章目录 JS 事件流机制详解&#xff1a;冒泡、捕获与完整事件流一、DOM 事件流基本概念二、事件捕获 (Event Capturing)特点代码示例 三、事件冒泡 (Event Bubbling)特点代码示例 四、完整事件流示例HTML 结构JavaScript…

算法专题七:分治

快排 1.颜色分类 题目链接:75. 颜色分类 - 力扣(LeetCode) class Solution {public void swap(int[] nums, int i, int j){int t = nums[i];nums[i] = nums[j];nums[j] = t;}public void sortColors(int[] nums) {int left=-1 ,i=0 ,right=nums.length;while(i<right){i…

Vue中虚拟DOM的原理与作用

绪论 首先我们先了解&#xff0c;DOM&#xff08;Document Object Model&#xff0c;文档对象模型&#xff09; 是浏览器对 HTML/XML 文档的结构化表示&#xff0c;它将文档解析为一个由节点&#xff08;Node&#xff09;和对象组成的树形结构&#xff08;称为 DOM 树&#xf…

使用vue3+ts+input封装上传组件,上传文件显示文件图标

效果图&#xff1a; 代码 <template><div class"custom-file-upload"><div class"upload"><!-- 显示已选择的文件 --><div class"file-list"><div v-for"(item, index) in state.filsList" :key&q…

【Linux】Ubuntu 创建应用图标的方式汇总,deb/appimage/通用方法

Ubuntu 创建应用图标的方式汇总&#xff0c;deb/appimage/通用方法 对于标准的 Ubuntu&#xff08;使用 GNOME 桌面&#xff09;&#xff0c;desktop 后缀的桌面图标文件主要保存在以下三个路径&#xff1a; 当前用户的桌面目录&#xff08;这是最常见的位置&#xff09;。所…