Go语言中的并发与多线程:进阶篇

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

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

开始课程

Go语言中的并发与多线程:进阶篇

\n\n

并发(concurrency)是Go语言的关键特性之一,它在运行时和语法层面被内置于语言架构中。与许多其他语言不同,这些语言的多线程通过复杂的库实现,并需要手动管理线程,而Go提供了轻量级的goroutine和channel。在进阶层面,重要的不仅仅是启动多个任务,而是合理地同步它们,避免数据竞争,并高效利用系统资源。在本文中,我们将探讨Go语言中并发编程的进阶技巧。

\n\n

1. Goroutine:生命周期管理与优先级

\n\n

Goroutine是由Go运行时管理的轻量级线程。它们通过关键字go启动。然而,在进阶层面,你需要理解如何控制它们的执行与结束。

\n\n

基本的goroutine启动:

\n
package main\n\nimport (\n    "fmt"\n    "time"\n)\n\nfunc worker(id int) {\n    fmt.Printf("Worker %d starting\\n", id)\n    time.Sleep(time.Second)\n    fmt.Printf("Worker %d done\\n", id)\n}\n\nfunc main() {\n    for i := 1; i <= 5; i++ {\n        go worker(i)\n    }\n    time.Sleep(2 * time.Second) // 临时方案:等待完成\n}
\n\n

问题:在这个例子中,我们使用time.Sleep来等待,这并不可靠。在进阶层面,会使用channel和sync.WaitGroup。

\n\n

2. 使用sync.WaitGroup进行同步

\n\n

sync.WaitGroup允许你等待指定数量的goroutine完成。这是一个基础但强大的同步工具。

\n\n
package main\n\nimport (\n    "fmt"\n    "sync"\n    "time"\n)\n\nfunc worker(id int, wg *sync.WaitGroup) {\n    defer wg.Done() // 完成时减少计数器\n    fmt.Printf("Worker %d starting\\n", id)\n    time.Sleep(time.Second)\n    fmt.Printf("Worker %d done\\n", id)\n}\n\nfunc main() {\n    var wg sync.WaitGroup\n    for i := 1; i <= 5; i++ {\n        wg.Add(1) // 启动前增加计数器\n        go worker(i, &wg)\n    }\n    wg.Wait() // 阻塞直到计数器归零\n    fmt.Println("All workers completed")\n}
\n\n

重要提示:wg.Add()必须在goroutine启动前调用,否则可能导致数据竞争。在goroutine内部使用defer wg.Done()以确保即使在发生panic时也能减少计数器。

\n\n

3. Channel:缓冲、方向与select

\n\n

Channel是goroutine之间数据交换的主要机制。在进阶层面,会使用缓冲channel、单向channel以及select语句。

\n\n

3.1. 缓冲channel

\n

缓冲channel允许在不立即接收的情况下发送数据。这减少了阻塞并提高了吞吐量。

\n
package main\n\nimport "fmt"\n\nfunc main() {\n    ch := make(chan int, 3) // 缓冲区大小为3的channel\n    ch <- 1\n    ch <- 2\n    ch <- 3\n    // ch <- 4 // 阻塞,因为缓冲区已满\n    fmt.Println(<-ch)\n    fmt.Println(<-ch)\n    fmt.Println(<-ch)\n}
\n\n

3.2. 单向channel

\n

为了类型安全和明确指定channel的角色,可以使用单向channel:chan<-(仅发送)和<-chan(仅接收)。

\n
package main\n\nimport "fmt"\n\nfunc sender(out chan<- int) {\n    for i := 0; i < 5; i++ {\n        out <- i\n    }\n    close(out)\n}\n\nfunc receiver(in <-chan int) {\n    for val := range in {\n        fmt.Println(val)\n    }\n}\n\nfunc main() {\n    ch := make(chan int)\n    go sender(ch)\n    receiver(ch)\n}
\n\n

3.3. select语句

\n

select允许一个goroutine同时等待多个channel。这是

博客

书籍推荐