当前位置: 首页 > news >正文

Go并发编程实战:深入理解Goroutine与Channel

Go并发编程实战:深入理解Goroutine与Channel

  • Go并发编程实战:深入理解Goroutine与Channel
    • 概述
    • 1. 为什么是Go的并发?从“线程”与“协程”说起
    • 2. Goroutine:如何使用?
    • 3. Channel:Goroutine间的安全通信
      • 创建与使用
      • 缓冲与同步
      • 经典模式:Range和Close
    • 4. 实战项目:构建一个并发Web爬虫
      • 核心功能
      • 代码实现
      • 如何运行
    • 5. 延伸学习与资源推荐

Go并发编程实战:深入理解Goroutine与Channel

概述

Go语言(Golang)凭借其简洁的语法、强大的标准库和原生支持的并发模型,在现代软件开发中占据了重要地位,尤其在云计算、微服务和网络服务后端领域。其最引人注目的特性莫过于GoroutineChannel,它们提供了一种轻量级、安全且高效的并发编程方式,让你能轻松构建高性能的并发程序。本文将带你从入门到实战,彻底掌握Go的并发哲学。
在这里插入图片描述

1. 为什么是Go的并发?从“线程”与“协程”说起

在传统语言(如Java)中,我们使用**线程(Thread)**来实现并发。然而,系统线程创建和上下文切换开销大,且数量有限(通常一台机器几千个),管理和同步(通过锁)也非常复杂,容易出错。

Go语言引入了**Goroutine(Go协程)**的概念。它是一种由Go运行时(runtime)管理的轻量级线程。

  • 开销极低:初始栈很小(约2KB),可以根据需要动态伸缩。你可以轻松创建数十万甚至上百万个Goroutine而耗尽内存。
  • 调度高效:Go运行时内置了一个强大的调度器(Scheduler),它在少量系统线程上复用Goroutine。当某个Goroutine发生阻塞(如I/O操作)时,调度器会立刻将其挂起,并在同一个线程上运行其他Goroutine。这一切对开发者透明,极大提高了CPU利用率。

简单来说,Goroutine让你可以用同步的方式写异步的代码,以极低的成本获得极高的并发能力。

2. Goroutine:如何使用?

使用Goroutine非常简单,只需在普通的函数调用前加上关键字 go

package mainimport ("fmt""time"
)func say(s string) {for i := 0; i < 3; i++ {time.Sleep(100 * time.Millisecond)fmt.Println(s)}
}func main() {// 在一个新的Goroutine中运行say函数go say("world")// 在主Goroutine中运行say函数say("hello")
}

运行上面的代码,你会看到"hello"和"world"交错打印,这表明两个函数在同时执行。注意:主Goroutine结束后,所有其他Goroutine会立即被终止,因此你可能需要一些同步机制来等待它们完成(如使用sync.WaitGroup)。

3. Channel:Goroutine间的安全通信

Goroutine是并发执行的,它们之间如何安全地传递数据和协调工作呢?答案是 Channel(通道)

Channel是一种类型化的管道,你可以通过它用操作符 <- 发送和接收值。

创建与使用

ch := make(chan int) // 创建一个传递int类型的通道// 在Goroutine中向通道发送数据
go func() {ch <- 42 // 将42发送到通道ch
}()// 从通道接收数据
value := <-ch
fmt.Println(value) // 输出: 42

缓冲与同步

默认通道是**无缓冲(Unbuffered)的:发送操作会一直阻塞,直到有另一个Goroutine执行接收操作,这是一种强大的同步机制。
你也可以创建
带缓冲(Buffered)**的通道:

ch := make(chan int, 2) // 缓冲区大小为2
ch <- 1
ch <- 2
// ch <- 3 // 如果此时再发送,就会阻塞,因为缓冲区满了fmt.Println(<-ch) // 1
fmt.Println(<-ch) // 2

经典模式:Range和Close

发送者可以通过close函数关闭通道,表示没有更多值会被发送。接收者可以使用range循环来持续接收值,直到通道被关闭。

func fibonacci(n int, c chan int) {x, y := 0, 1for i := 0; i < n; i++ {c <- xx, y = y, x+y}close(c) // 关闭通道
}func main() {c := make(chan int, 10)go fibonacci(cap(c), c)// 使用range循环从通道中接收数据,直到通道被关闭for i := range c {fmt.Println(i)}
}

4. 实战项目:构建一个并发Web爬虫

让我们利用所学知识,构建一个简单的、并发安全的Web爬虫。它的功能是并发地抓取一组URL,并统计每个页面的大小。

核心功能

  • 并发地发送HTTP GET请求获取多个URL的内容。
  • 使用Channel来收集结果。
  • 使用WaitGroup来等待所有抓取任务完成。

代码实现

package mainimport ("fmt""io""log""net/http""sync""time"
)// FetchResult 用于存放抓取结果
type FetchResult struct {Url  stringSize int64Err  error
}// fetchUrl 抓取单个URL,将结果发送到channel
func fetchUrl(url string, ch chan<- FetchResult, wg *sync.WaitGroup) {defer wg.Done() // 通知WaitGroup,当前Goroutine已完成start := time.Now()resp, err := http.Get(url)if err != nil {ch <- FetchResult{Url: url, Err: err}return}defer resp.Body.Close()body, err := io.ReadAll(resp.Body)if err != nil {ch <- FetchResult{Url: url, Err: err}return}elapsed := time.Since(start)size := int64(len(body))ch <- FetchResult{Url: url, Size: size, Err: nil}log.Printf("Fetched %s (%d bytes) in %s", url, size, elapsed)
}func main() {urls := []string{"https://www.google.com","https://www.github.com","https://www.stackoverflow.com","https://go.dev","https://medium.com",}// 创建一个带缓冲的Channel存放结果resultCh := make(chan FetchResult, len(urls))// 创建一个WaitGroup来等待所有Goroutine完成var wg sync.WaitGroup// 为每个URL启动一个Goroutinefor _, url := range urls {wg.Add(1) // WaitGroup计数器+1go fetchUrl(url, resultCh, &wg)}// 启动一个单独的Goroutine,等待所有抓取任务完成后关闭Channelgo func() {wg.Wait()close(resultCh)}()// 主Goroutine从Channel中读取并处理所有结果fmt.Println("\n=== Fetch Results ===")for result := range resultCh {if result.Err != nil {fmt.Printf("Failed to fetch %s: %v\n", result.Url, result.Err)} else {fmt.Printf("Fetched %s: %d bytes\n", result.Url, result.Size)}}
}

如何运行

  1. 将代码保存为 concurrent_crawler.go
  2. 在终端运行:go run concurrent_crawler.go

这个项目完美展示了如何用Goroutine实现“fork-join”并发模式,如何使用Channel进行通信,以及如何使用WaitGroup进行同步,是Go并发编程的经典范例。

5. 延伸学习与资源推荐

  • 官方资源:

    • The Go Programming Language Tour (Go语言之旅):官方交互式教程,非常适合初学者。
    • Effective Go:编写优雅、地道的Go代码的最佳实践。
  • 经典书籍:

    • 《The Go Programming Language》 (Alan A. A. Donovan & Brian W. Kernighan):被誉为Go语言的“圣经”,深度和广度俱佳。
  • 进阶主题:

    • Context包:用于在Goroutine之间传递截止时间、取消信号以及其他请求范围的值,是构建网络服务的关键。
    • Sync包:提供了基本的同步原语,如互斥锁(Mutex)、读写锁(RWMutex)等,用于更底层的同步控制。
    • 并发模式:学习select语句、超时控制、管道(Pipeline)、工作池(Worker Pool)等高级模式。

Go的并发模型是其灵魂所在。掌握Goroutine和Channel,你就能充分利用现代多核CPU的优势,轻松构建出高性能、高可靠性的服务。祝你学习愉快!


文章转载自:

http://TmMYtRCt.cptzd.cn
http://hvENouvK.cptzd.cn
http://60LZnhYG.cptzd.cn
http://mW8nGNDj.cptzd.cn
http://5NU1zBSA.cptzd.cn
http://r4D814us.cptzd.cn
http://oRAIYYek.cptzd.cn
http://NsFJTbGA.cptzd.cn
http://5J6NzpFT.cptzd.cn
http://ggZmf4CN.cptzd.cn
http://SeNjg1Mh.cptzd.cn
http://muq6QDQB.cptzd.cn
http://WglJYZxp.cptzd.cn
http://myIjlKHO.cptzd.cn
http://wldAmOcN.cptzd.cn
http://CFNKLZhL.cptzd.cn
http://Us6jsqUX.cptzd.cn
http://ViutIWHX.cptzd.cn
http://KoEUkRLW.cptzd.cn
http://KNzYPHz7.cptzd.cn
http://orZ2o1GE.cptzd.cn
http://n4FFb2qC.cptzd.cn
http://vtkFhvCS.cptzd.cn
http://7YwKA8nw.cptzd.cn
http://ISgzQgj2.cptzd.cn
http://s9DY9Fpw.cptzd.cn
http://dbs0Ajdb.cptzd.cn
http://dvyJC4lC.cptzd.cn
http://n8MYHDRI.cptzd.cn
http://14s6r98j.cptzd.cn
http://www.dtcms.com/a/382783.html

相关文章:

  • 嵌入式硬件设计
  • (附源码)基于Spring Boot社区“邻里帮”平台的设计与实现
  • 贪心算法java
  • AI问答-Nuxt4:什么时候发布的,有哪些特性,和Nuxt3相比 有哪些优势 / Nuxt4 / Nuxt-v4
  • MyBatis 从入门到精通(第三篇)—— 动态 SQL、关联查询与查询缓存
  • 10 C++map/set的底层数据结构红黑树它来了,红黑树入门全解。
  • 【iOS】ViewController的生命周期
  • 数据库基础-01
  • 免费无版权!PPT图标素材的6个优质获取渠道
  • 【STL库】map/set 的封装原理
  • 市面上各类USB无线抓包网卡测试与收录(握手包抓包/无线监听)
  • 基于bang-bang起停式算法的交流电机FOC控制系统simulink建模与模拟仿真
  • 使用HTTPS 服务在浏览器端使用摄像头的方式解析
  • AI 机器视觉检测方案:破解食物包装四大质检难题,筑牢食品安全防线
  • Science Advances--3D打印生物启发扭曲双曲超材料,用于无人机冲击缓冲和自供电实时传感
  • HarmonyOS生态开发核心工具技术介绍及关于CSDN增加ArkTS等标签建议
  • 【算法笔记】堆和堆排序
  • 电商导购系统的微服务监控体系:基于Prometheus与Grafana的可视化方案
  • fMoE论文阅读笔记
  • 721SJBH笔记本电脑销售网站
  • k3s集群部署(使用外部etcd集群)
  • 京东返利app的分布式ID生成策略:雪花算法在订单系统中的实践
  • 大数据分析岗位发展前景与行业需求分析
  • 【Linux手册】共享内存:零拷贝实现共享的优势与实操指南
  • ARM的TrustZone
  • 返利app排行榜的缓存更新策略:基于过期时间与主动更新的混合方案
  • springboot+zookeeper+(2025最新)Dubbo-admin实现分布式
  • 缓存与数据库一致性实战手册:从故障修复到架构演进
  • 基于 Linux 内核模块的字符设备 FIFO 驱动设计与实现解析(C/C++代码实现)
  • 【C++】类和对象(下):初始化列表、类型转换、Static、友元、内部类、匿名对象/有名对象、优化