Go语言实战案例:使用 Goroutine 并发打印

简介: 本文通过简单案例讲解 Go 语言核心并发模型 Goroutine,涵盖协程启动、输出控制、主程序退出机制,并结合 sync.WaitGroup 实现并发任务同步,帮助理解 Go 并发设计思想与实际应用。

 

在本篇文章中,我们将从最基础的 Goroutine 并发模型入手,逐步掌握 Go 语言中并发控制的核心思想。通过一个简单的“并发打印任务”案例,掌握 Goroutine 的使用、输出顺序的控制、主协程退出时机等关键知识。


一、为什么学习 Goroutine?

Goroutine 是 Go 语言最核心的并发原语。相比于线程,它更加轻量:

  • • 启动成本极低(大约只需 2KB 内存)
  • • 数量可以轻松达到上万甚至百万级
  • • 使用非常简单,只需一个 go 关键字

Go 的并发理念是:不要通过共享内存来通信,而要通过通信来共享内存。

掌握 Goroutine,是理解 Go 并发模型的第一步。


二、实战目标

我们将实现一个小项目:

  1. 1. 启动多个 Goroutine 并发打印任务信息
  2. 2. 每个任务打印固定内容
  3. 3. 确保主程序等待所有任务完成
  4. 4. 引入 sync.WaitGroup 实现同步控制

三、基础版本:启动多个 Goroutine

下面是最简单的 Goroutine 并发打印实现:

package main
import (
    "fmt"
    "time"
)
func printTask(id int) {
    fmt.Printf("任务 %d 正在执行\n", id)
}
func main() {
    for i := 1; i <= 5; i++ {
        go printTask(i)
    }
    // 给协程一点时间完成(不推荐的方式)
    time.Sleep(time.Second)
    fmt.Println("主程序结束")
}

运行结果(示例):

任务 2 正在执行
任务 4 正在执行
任务 1 正在执行
任务 3 正在执行
任务 5 正在执行
主程序结束

你会注意到,输出顺序可能与代码中的顺序不同,这是并发执行的结果。


四、问题:主程序过早退出

在上面例子中我们使用了 time.Sleep 来“凑时间”,这是一种不可靠且危险的方式。如果任务执行时间更长,就会被主程序强制退出。

为了解决这个问题,我们引入 sync.WaitGroup


五、进阶版本:使用 WaitGroup 等待所有 Goroutine 完成

package main
import (
    "fmt"
    "sync"
    "time"
)
// 模拟执行任务
func printTask(id int, wg *sync.WaitGroup) {
    defer wg.Done() // 标记一个任务完成
    fmt.Printf("任务 %d 开始执行\n", id)
    time.Sleep(time.Duration(id) * 200 * time.Millisecond) // 模拟不同任务耗时
    fmt.Printf("任务 %d 执行完毕\n", id)
}
func main() {
    var wg sync.WaitGroup
    taskCount := 5
    for i := 1; i <= taskCount; i++ {
        wg.Add(1) // 增加一个任务
        go printTask(i, &wg)
    }
    wg.Wait() // 等待所有任务完成
    fmt.Println("所有任务完成,主程序退出")
}

输出示例:

任务 1 开始执行
任务 2 开始执行
任务 3 开始执行
任务 4 开始执行
任务 5 开始执行
任务 1 执行完毕
任务 2 执行完毕
任务 3 执行完毕
任务 4 执行完毕
任务 5 执行完毕
所有任务完成,主程序退出

六、重点知识点解析

1. go 关键字

只需在函数前加 go,即可将该函数交由调度器异步执行,立即返回

2. sync.WaitGroup

  • wg.Add(n):添加 n 个任务
  • wg.Done():在任务完成后调用
  • wg.Wait():阻塞主线程,直到所有任务完成

3. defer 用法

使用 defer wg.Done() 可以确保即使函数中途返回也能正确减计数。


七、延伸思考

  • • 若有 10万个任务要并发执行,Goroutine 是否能承载?(答案是:可以,但建议使用并发控制池)
  • • 如果某个任务失败,是否能提前取消其他任务?(答案是:可以结合 context 实现)

八、真实应用场景举例

  • • 批量调用多个接口(如:并发请求天气接口、发邮件)
  • • 批量处理文件(压缩、上传、转码)
  • • 并发爬虫(抓取多个页面)
  • • 消息队列并发消费任务

九、小结

本文通过“并发打印任务”这一简单示例,向你展示了 Go 并发编程的基本能力:

  • • 启动 Goroutine 的方法
  • • 并发任务间的非确定性
  • • 如何使用 WaitGroup 来保证主程序的正确退出
  • • Goroutine 的轻量特性,适合高并发场景

并发是 Go 的灵魂,控制好协程的生命周期和调度能力,将直接影响系统的健壮性与性能。

 

相关文章
|
2月前
|
Linux Go iOS开发
Go语言100个实战案例-进阶与部署篇:使用Go打包生成可执行文件
本文详解Go语言打包与跨平台编译技巧,涵盖`go build`命令、多平台构建、二进制优化及资源嵌入(embed),助你将项目编译为无依赖的独立可执行文件,轻松实现高效分发与部署。
|
3月前
|
数据采集 数据挖掘 测试技术
Go与Python爬虫实战对比:从开发效率到性能瓶颈的深度解析
本文对比了Python与Go在爬虫开发中的特点。Python凭借Scrapy等框架在开发效率和易用性上占优,适合快速开发与中小型项目;而Go凭借高并发和高性能优势,适用于大规模、长期运行的爬虫服务。文章通过代码示例和性能测试,分析了两者在并发能力、错误处理、部署维护等方面的差异,并探讨了未来融合发展的趋势。
321 0
|
2月前
|
存储 前端开发 JavaScript
Go语言实战案例-项目实战篇:编写一个轻量级在线聊天室
本文介绍如何用Go语言从零实现一个轻量级在线聊天室,基于WebSocket实现实时通信,支持多人消息广播。涵盖前后端开发、技术选型与功能扩展,助你掌握Go高并发与实时通信核心技术。
|
3月前
|
负载均衡 监控 Java
微服务稳定性三板斧:熔断、限流与负载均衡全面解析(附 Hystrix-Go 实战代码)
在微服务架构中,高可用与稳定性至关重要。本文详解熔断、限流与负载均衡三大关键技术,结合API网关与Hystrix-Go实战,帮助构建健壮、弹性的微服务系统。
457 1
微服务稳定性三板斧:熔断、限流与负载均衡全面解析(附 Hystrix-Go 实战代码)
|
3月前
|
安全 Go 开发者
Go语言实战案例:使用sync.Mutex实现资源加锁
在Go语言并发编程中,数据共享可能导致竞态条件,使用 `sync.Mutex` 可以有效避免这一问题。本文详细介绍了互斥锁的基本概念、加锁原理及实战应用,通过构建并发安全的计数器演示了加锁与未加锁的区别,并封装了一个线程安全的计数器结构。同时对比了Go中常见的同步机制,帮助开发者理解何时应使用 `Mutex` 及其注意事项。掌握 `Mutex` 是实现高效、安全并发编程的重要基础。
|
11月前
|
并行计算 安全 Go
Go语言中的并发编程:掌握goroutines和channels####
本文深入探讨了Go语言中并发编程的核心概念——goroutine和channel。不同于传统的线程模型,Go通过轻量级的goroutine和通信机制channel,实现了高效的并发处理。我们将从基础概念开始,逐步深入到实际应用案例,揭示如何在Go语言中优雅地实现并发控制和数据同步。 ####
|
存储 Go 开发者
Go语言中的并发编程与通道(Channel)的深度探索
本文旨在深入探讨Go语言中并发编程的核心概念和实践,特别是通道(Channel)的使用。通过分析Goroutines和Channels的基本工作原理,我们将了解如何在Go语言中高效地实现并行任务处理。本文不仅介绍了基础语法和用法,还深入讨论了高级特性如缓冲通道、选择性接收以及超时控制等,旨在为读者提供一个全面的并发编程视角。
278 50
|
12月前
|
安全 Serverless Go
Go语言中的并发编程:深入理解与实践####
本文旨在为读者提供一个关于Go语言并发编程的全面指南。我们将从并发的基本概念讲起,逐步深入到Go语言特有的goroutine和channel机制,探讨它们如何简化多线程编程的复杂性。通过实例演示和代码分析,本文将揭示Go语言在处理并发任务时的优势,以及如何在实际项目中高效利用这些特性来提升性能和响应速度。无论你是Go语言的初学者还是有一定经验的开发者,本文都将为你提供有价值的见解和实用的技巧。 ####
|
算法 安全 程序员
Go语言的并发编程:深入理解与实践####
本文旨在探讨Go语言在并发编程方面的独特优势及其实现机制,通过实例解析关键概念如goroutine和channel,帮助开发者更高效地利用Go进行高性能软件开发。不同于传统的摘要概述,本文将以一个简短的故事开头,引出并发编程的重要性,随后详细阐述Go语言如何简化复杂并发任务的处理,最后通过实际案例展示其强大功能。 --- ###
|
Go 开发者
Go语言中的并发编程:掌握goroutines和channels####
本文深入探讨了Go语言中并发编程的核心概念,重点介绍了goroutines和channels的工作原理及其在实际开发中的应用。文章通过实例演示如何有效地利用这些工具来编写高效、可维护的并发程序,旨在帮助读者理解并掌握Go语言在处理并发任务时的强大能力。 ####