Golang 并发编程:掌握 goroutine 和 channel

# Golang 并发编程:掌握 goroutine 和 channel

## 什么是并发编程?

并发编程是指同时执行多个任务的编程方式。在 Go 语言中,并发编程是其核心特性之一,通过 goroutine 和 channel 实现。

## goroutine

### 什么是 goroutine?

goroutine 是 Go 语言中的轻量级线程,由 Go 运行时管理。与传统线程相比,goroutine 的创建和切换成本非常低,因此可以创建大量的 goroutine。

### 创建 goroutine

“`go
// 启动一个 goroutine
func main() {
go sayHello()
fmt.Println(“Main function”)
}

func sayHello() {
fmt.Println(“Hello from goroutine”)
}
“`

### goroutine 的特性

– goroutine 是轻量级的,创建成本低
– goroutine 的调度由 Go 运行时负责
– goroutine 之间共享内存
– 当主 goroutine 结束时,所有其他 goroutine 也会结束

## channel

### 什么是 channel?

channel 是 goroutine 之间通信的管道,可以用于在 goroutine 之间传递数据。

### 创建 channel

“`go
// 创建无缓冲通道
ch := make(chan int)

// 创建带缓冲通道
ch := make(chan int, 10)
“`

### 通道的操作

#### 发送数据

“`go
ch <- 10 // 发送 10 到通道 ``` #### 接收数据 ```go // 接收数据 value := <-ch // 非阻塞接收 value, ok := <-ch if ok { fmt.Println("Received:", value) } else { fmt.Println("Channel closed") } ``` #### 关闭通道 ```go close(ch) ``` ### 通道的特性 - 通道是引用类型 - 无缓冲通道的发送和接收操作会阻塞 - 带缓冲通道在缓冲区满时发送会阻塞,在缓冲区空时接收会阻塞 - 关闭通道后,不能再发送数据,但可以继续接收数据 ## 并发模式 ### 等待多个 goroutine 完成 #### 使用 sync.WaitGroup ```go func main() { var wg sync.WaitGroup for i := 0; i < 5; i++ { wg.Add(1) go func(i int) { defer wg.Done() fmt.Println("Goroutine", i) }(i) } wg.Wait() fmt.Println("All goroutines completed") } ``` ### 单向通道 ```go // 只发送通道 type Sender chan<- int // 只接收通道 type Receiver <-chan int func send(s Sender) { s <- 10 } func receive(r Receiver) { value := <-r fmt.Println(value) } ``` ### 通道的选择 ```go func main() { ch1 := make(chan int) ch2 := make(chan int) go func() { time.Sleep(1 * time.Second) ch1 <- 1 }() go func() { time.Sleep(2 * time.Second) ch2 <- 2 }() for i := 0; i < 2; i++ { select { case value := <-ch1: fmt.Println("Received from ch1:", value) case value := <-ch2: fmt.Println("Received from ch2:", value) case <-time.After(3 * time.Second): fmt.Println("Timeout") return } } } ``` ## 并发安全 ### 互斥锁 ```go var ( counter int mutex sync.Mutex ) func increment() { mutex.Lock() defer mutex.Unlock() counter++ } func getCounter() int { mutex.Lock() defer mutex.Unlock() return counter } ``` ### 读写锁 ```go var ( counter int rwMutex sync.RWMutex ) func increment() { rwMutex.Lock() defer rwMutex.Unlock() counter++ } func getCounter() int { rwMutex.RLock() defer rwMutex.RUnlock() return counter } ``` ### 原子操作 ```go var counter int32 func increment() { atomic.AddInt32(&counter, 1) } func getCounter() int32 { return atomic.LoadInt32(&counter) } ``` ## 并发最佳实践 1. **使用 goroutine 处理并发任务**:将独立的任务放在 goroutine 中执行 2. **使用 channel 进行通信**:通过 channel 在 goroutine 之间传递数据,避免共享内存 3. **使用 sync.WaitGroup 等待 goroutine 完成**:确保所有 goroutine 都完成后再继续执行 4. **避免死锁**:确保通道的发送和接收操作能够正确配对 5. **使用缓冲通道**:适当使用缓冲通道可以减少阻塞,提高性能 6. **使用 select 处理多个通道**:使用 select 语句可以同时监听多个通道的操作 7. **注意并发安全**:对共享资源的访问要使用互斥锁或原子操作 ## 实例:并发下载文件 ```go func main() { urls := []string{ "https://example.com/file1.txt", "https://example.com/file2.txt", "https://example.com/file3.txt", } var wg sync.WaitGroup for _, url := range urls { wg.Add(1) go func(url string) { defer wg.Done() downloadFile(url) }(url) } wg.Wait() fmt.Println("All files downloaded") } func downloadFile(url string) { resp, err := http.Get(url) if err != nil { fmt.Println("Error downloading", url, err) return } defer resp.Body.Close() filename := filepath.Base(url) file, err := os.Create(filename) if err != nil { fmt.Println("Error creating file", err) return } defer file.Close() _, err = io.Copy(file, resp.Body) if err != nil { fmt.Println("Error copying file", err) return } fmt.Println("Downloaded", url) } ``` ## 总结 本文介绍了 Go 语言中的并发编程,包括 goroutine、channel、并发模式和并发安全等内容。Go 语言的并发模型基于 CSP(Communicating Sequential Processes)理论,通过 goroutine 和 channel 实现了高效的并发编程。掌握这些并发编程技术,对于编写高性能、可扩展的 Go 应用程序至关重要。在实际开发中,我们需要根据具体场景选择合适的并发模式,确保程序的正确性和性能。

Scroll to Top