深入探究 Go 中的并发性(不使用 Channels)
Go 的并发模型是其最突出的功能之一。虽然通道经常成为 goroutine 通信的焦点,但理解并发的核心,而不依赖通道,对于掌握 Go 至关重要。这篇文章深入探讨了 goroutine、`sync` 包和同步的实用模式。
并发与并行
并发是指同时管理多个任务,而并行是指同时执行多个任务。Go 专为并发而设计,因此可以轻松构建能够独立处理多个操作的程序。
Goroutines:构建块
**goroutine** 是 Go 运行时管理的轻量级线程。创建 goroutine 非常简单,只需在函数调用前加上“go”前缀即可。
package main
import (
"fmt"
"time"
)
func task(name string) {
for i := 0; i < 5; i++ {
fmt.Printf("Task %s is running: %d\n", name, i)
time.Sleep(time.Millisecond * 500)
}
}
func main() {
go task("A")
go task("B")
// Let the main function wait for goroutines to finish
time.Sleep(time.Second * 3)
fmt.Println("Main function exiting")
}无通道同步
使用 sync.WaitGroup
`sync.WaitGroup` 是一个强大的工具,可以等待多个 goroutine 完成其工作。
package main
import (
"fmt"
"sync"
"time"
)
func worker(id int, wg *sync.WaitGroup) {
defer wg.Done() // Decrement the counter when the goroutine completes
fmt.Printf("Worker %d starting\n", id)
time.Sleep(time.Second)
fmt.Printf("Worker %d done\n", id)
}
func main() {
var wg sync.WaitGroup
for i := 1; i <= 3; i++ {
wg.Add(1)
go worker(i, &wg)
}
wg.Wait() // Wait for all goroutines to finish
fmt.Println("All workers completed")
}使用 sync.Mutex
当多个 goroutine 访问共享数据时,可能会发生竞争条件。`sync.Mutex` 可确保一次只有一个 goroutine 可以访问代码的关键部分。
package main
import (
"fmt"
"sync"
)
type Counter struct {
value int
mu sync.Mutex
}
func (c *Counter) Increment() {
c.mu.Lock()
c.value++
c.mu.Unlock()
}
func main() {
counter := &Counter{}
var wg sync.WaitGroup
for i := 0; i < 10; i++ {
wg.Add(1)
go func() {
defer wg.Done()
counter.Increment()
}()
}
wg.Wait()
fmt.Printf("Final Counter Value: %d\n", counter.value)
}实用模式
无通道的工作池
工作池是一种模式,多个工作器同时执行任务。工作器可以从受互斥锁保护的共享切片访问任务,而不是通过通道。
package main
import (
"fmt"
"sync"
)
func worker(id int, tasks *[]int, mu *sync.Mutex, wg *sync.WaitGroup) {
defer wg.Done()
for {
mu.Lock()
if len(*tasks) == 0 {
mu.Unlock()
return
}
task := (*tasks)[0]
*tasks = (*tasks)[1:]
mu.Unlock()
fmt.Printf("Worker %d processing task %d\n", id, task)
}
}
func main() {
tasks := []int{1, 2, 3, 4, 5}
var mu sync.Mutex
var wg sync.WaitGroup
for i := 1; i <= 3; i++ {
wg.Add(1)
go worker(i, &tasks, &mu, &wg)
}
wg.Wait()
fmt.Println("All tasks processed")
}并发陷阱
结论
即使没有通道,Go 中的并发功能也非常强大。通过掌握 goroutines、`sync` 包和常见模式,您可以编写高效、高性能的程序。试用这些工具,您很快就会明白为什么 Go 是并发编程的首选!