Go语言中的并发与多线程:goroutines和通道

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

Go 语言并发入门

Go 语言(Golang)由 Google 开发,充分考虑了现代性能和可扩展性需求。Go 的关键特性之一是其内置的 并发(concurrency)支持——即程序同时执行多个任务的能力。与传统的多线程(由操作系统管理线程)不同,Go 提供了轻量级的 goroutines 和便捷的同步原语——通道。

本文将介绍:

  • 什么是 goroutines 以及如何启动它们
  • 用于 goroutines 间数据交换的通道
  • 用于安全访问共享资源的互斥锁
  • 并发代码的实践示例和模式


1. Goroutines:Go 的轻量级线程

Goroutine 是一个与其他 goroutines 并发执行的函数。它仅占用几 KB 的栈空间(而 OS 线程的栈可能达到 MB 级别)。只需在函数调用前添加关键字 go 即可启动一个 goroutine。



示例:启动 goroutines

package main

import ( "fmt" "time")

func sayHello(name string) { for i := 0; i < 3; i++ { fmt.Printf("你好,%s! (迭代 %d)", name, i) time.Sleep(100 * time.Millisecond) }}

func main() { go sayHello("Alice") // 启动 goroutine go sayHello("Bob") // 再启动一个 goroutine time.Sleep(1 * time.Second) // 等待完成 fmt.Println("主函数执行完毕")}

注意: 当 main 函数结束时,程序会终止,即使其他 goroutines 仍在运行。因此我们使用 time.Sleep 来等待。在实际代码中,最好使用 sync.WaitGroup。



使用 sync.WaitGroup

package main

import ( "fmt" "sync")

func worker(id int, wg *sync.WaitGroup) { defer wg.Done() // 完成时减少计数器 fmt.Printf("工作者 %d 开始工作", id) // ... 执行某些任务 ... fmt.Printf("工作者 %d 结束工作", id)}

func main() { var wg sync.WaitGroup

for i := 1; i <= 3; i++ { wg.Add(1) // 启动 goroutine 前增加计数器 go worker(i, &wg) }

wg.Wait() // 阻塞 main 直到所有 goroutines 完成 fmt.Println("所有工作者已完成工作")}


2. 通道:goroutines 间的数据交换

通道(channels)是类型化的“管道”,goroutines 可以通过它发送和接收值。通道无需显式锁定即可实现安全的同步。



创建和使用通道

package main

import "fmt"

func main() { // 创建一个整数类型的通道 ch := make(chan int)

// 启动一个发送数据的 goroutine go func() { ch <- 42 // 向通道发送值 }()

// 从通道接收值 value := <-ch fmt.Println("接收到:", value)}

默认情况下,通道是 无缓冲 的:发送会阻塞,直到接收方准备好接收数据,反之亦然。这确保了同步。



带缓冲的通道

package main

import "fmt"

func main() { // 创建一个容量为 3 的缓冲通道 ch := make(chan string, 3)

// 无阻塞发送(直到缓冲区满) ch <- "one" ch <- "two" ch <- "three"

// 读取数据 fmt.Println(<-ch) fmt.Println(<-ch) fmt.Println(<-ch)}


关闭通道与 range 循环

package main

import "fmt"

func fibonacci(n int, ch chan int) { a, b := 0, 1 for i := 0; i < n; i++ { ch <-

博客

书籍推荐