1·基础入门入门

Hello World 与开发环境

setuphello-worldgo-mod

Go 并发编程 - 第 1 课:Hello World 与开发环境

1. 学习目标

通过学习本课,你将能够:

  • 成功安装Go语言开发环境并验证其是否工作正常。
  • 理解Go项目的基本结构,并使用 go mod 工具初始化一个新项目。
  • 理解并区分 go rungo build 命令的用途。
  • 编写并运行你的第一个Go程序(Hello, World!),以及一个简单的并发示例。
  • 掌握查阅Go标准库文档的基本方法。

2. 核心概念

开发环境与安装

Go语言拥有出色的跨平台支持。你需要从Go官方网站(golang.org)下载对应操作系统(Windows, macOS, Linux)的安装包,并按照指引完成安装。安装完成后,打开终端或命令行工具,输入 go version 命令。如果看到类似 go version go1.22.0 linux/amd64 的输出,恭喜你,环境已就绪。

项目结构与Go Modules

一个现代的Go项目,推荐使用 Go Modules 来管理依赖。go.mod 文件位于项目根目录,它记录了项目的模块路径和依赖关系。你可以使用 go mod init <模块名> 命令来初始化一个新模块。模块名通常是一个代码仓库的路径,例如 github.com/yourname/hello

代码结构

Go源代码文件以 .go 为后缀。每个 .go 文件的开头必须声明它所属的 package。一个可独立运行的程序必须包含一个 main 包和一个 main() 函数,作为程序的入口点。

go rungo build

  • go run <文件或包>: 编译并直接运行程序,不会生成二进制可执行文件。非常适合开发和调试。
  • go build [包]: 编译包及其依赖项,生成一个可执行的二进制文件(在Windows下是.exe,Linux/macOS下是同名文件)。

并发编程初探

Go语言的核心优势之一是其对并发的原生支持。本课将通过一个简单例子接触两个关键概念:

  • Goroutine: 轻量级线程,由Go运行时管理。使用 go 关键字加函数调用即可启动。
  • Channel: 用于在Goroutine之间进行通信和同步的管道。可以看作是一个类型安全的消息队列。

3. 代码示例

示例1:经典Hello, World!

// 文件名:main.go
// 这个文件属于 `main` 包
package main

// 导入 “fmt” 包,它提供了格式化I/O函数(如打印)
import "fmt"

// main 函数是程序的入口点
func main() {
    // 使用 fmt 包中的 Println 函数打印一行文本
    fmt.Println("Hello, World!")
    fmt.Println("欢迎来到Go并发编程的世界!")
}

如何运行:

  1. 在终端中进入 main.go 文件所在的目录。
  2. 执行命令:go run main.go

示例2:一个简单的并发示例

// 文件名:main.go
package main

import (
    "fmt"
    "time" // 引入time包,用于演示延时
)

// worker 函数模拟一个耗时的工作
// 参数 `id` 是工人编号, `jobs` 是接收任务的通道, `results` 是发送结果的通道
func worker(id int, jobs <-chan int, results chan<- int) {
    for j := range jobs { // 持续从 jobs 通道接收任务
        fmt.Printf("worker %d 开始处理任务 %d\n", id, j)
        time.Sleep(time.Second) // 模拟耗时工作
        fmt.Printf("worker %d 完成任务 %d\n", id, j)
        results <- j * 2 // 将处理结果(任务值乘以2)发送到 results 通道
    }
}

func main() {
    const numJobs = 5
    // 创建两个通道:jobs 用于分发任务,results 用于收集结果
    jobs := make(chan int, numJobs)
    results := make(chan int, numJobs)

    // 启动3个 worker goroutine
    for w := 1; w <= 3; w++ {
        go worker(w, jobs, results)
    }

    // 发送5个任务到 jobs 通道
    for j := 1; j <= numJobs; j++ {
        jobs <- j
    }
    close(jobs) // 所有任务已发送,关闭 jobs 通道

    // 收集所有结果
    for a := 1; a <= numJobs; a++ {
        fmt.Println("结果:", <-results)
    }
}

如何运行: go run main.go。观察输出,你会看到三个worker在并发地处理任务,且执行顺序是交错的。

4. 实践练习

练习1:自定义输出

创建一个新的Go模块,并编写一个程序,输出以下内容:

今天是学习Go语言的好日子!
当前时间是:<此处插入当前时间,格式为15:04:05>

提示: 你需要导入 fmttime 包。使用 time.Now().Format(“15:04:05”) 来格式化时间。

练习2:扩展并发示例

在示例2的基础上,修改程序,让每个worker在完成一个任务后,除了输出完成信息,还额外输出 "工人[id] 已累计完成 [n] 个任务"。你需要在 worker 函数内部维护一个计数器。 预期输出可能类似:

worker 1 开始处理任务 1
worker 2 开始处理任务 2
worker 3 开始处理任务 3
worker 2 完成任务 2
工人2 已累计完成 1 个任务
...

练习3:通道通信挑战

编写一个程序,包含两个goroutine:senderreceiver

  • sender 向一个字符串类型的通道依次发送 “Hello”, “Go”, “Concurrency”。
  • receiver 从该通道接收这三个字符串并依次打印出来。 预期输出:
收到消息: Hello
收到消息: Go
收到消息: Concurrency

5. 常见错误

  1. 忘记初始化Go Modules: 在项目目录外运行代码,或没有运行 go mod init,导致包管理混乱。始终在项目根目录开始你的工作。
  2. 代码写在了包的外面: 所有顶级函数、变量等声明都必须在某个包内。确保你的代码行在 package main 声明之后。
  3. 导入未使用的包: Go编译器不允许导入但不使用的包。如果导入了 “fmt” 却没用到,编译会报错。这是Go强制的代码整洁规范。
  4. 在并发示例中,主线程没有等待goroutine完成: 在简单的并发示例中,如果main函数在goroutine完成前就退出了,那么goroutine的工作将无法看到结果。虽然示例2通过读取结果通道自然同步了,但初学者常犯的错误是启动goroutine后就直接 os.Exit 或程序结束。可以使用 sync.WaitGroup 或确保通道操作完成来等待。
  5. 拼写错误: 函数名或包名大小写错误(如 println 应为 Println),或忘记引号。

6. 小结

本节课我们完成了Go并发编程之旅的第一步:

  • 环境搭建: 安装了Go并验证了环境。
  • 项目基础: 学习了使用 go mod init 初始化项目,理解了Go项目的代码结构。
  • 程序编译与运行: 掌握了 go rungo build 的基本用法。
  • 第一个程序: 成功运行了 “Hello, World!”,并接触了并发编程的基石——goroutinechannel
  • 工具使用: 了解了查阅Go标准库文档的重要性。

你已经搭建好了舞台,并成功演出了第一幕。接下来,我们将深入学习Go语言的基石——变量与基本类型,这是构建任何复杂程序的起点。

练习编辑器

go
Loading...

继续学习

完成本课后,建议继续学习下一课「变量与基本类型」 以巩固所学知识。