Golang并发编程学习指南
Golang并发编程学习指南 大家好,
正在学习并发编程,想了解通道和等待组的使用场景。
// var wait sync.WaitGroup
type Post struct {
ID int
Text string
}
func main() {
var posts []Post
p1 := CreatePost(1, "yahoo")
p2 := CreatePost(2, "google 1")
p3 := CreatePost(3, "excite 2")
p4 := CreatePost(4, "altavista 3")
p5 := CreatePost(5, "bing 4")
posts = append(posts, p1, p2, p3, p4, p5)
postCh := make(chan Post, 5)
go GetPosts(postCh, posts)
for v := range postCh {
fmt.Println(v)
}
}
func GetPosts(c chan Post, po []Post) {
for _, v := range po {
c <- v
}
close(c)
}
func CreatePost(id int, text string) Post {
return Post{
ID: id,
Text: text,
}
}
更多关于Golang并发编程学习指南的实战教程也可以访问 https://www.itying.com/category-94-b0.html
2 回复
通道的用例:在goroutine之间传递值(而不是在goroutine之间共享内存)。
无缓冲通道的用例:在两个goroutine之间提供同步。(发送方会阻塞,直到接收方准备好接收发送的元素。)
WaitGroups的用例:生成多个goroutine并等待它们全部完成。
当然,你可以用通道替代WaitGroups,也可以用受互斥锁保护的共享变量访问来替代通道。但通道和等待组更易于阅读、理解和推理。
更多关于Golang并发编程学习指南的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
以下是针对您代码中通道和等待组使用场景的专业分析,以及改进后的示例代码。
通道和等待组的使用场景
通道(channel) 主要用于goroutine之间的数据传递和同步,特别适合生产者-消费者模式。等待组(sync.WaitGroup) 则用于等待一组goroutine完成执行。
在您的代码中,虽然使用了通道来传递数据,但缺少对goroutine执行完成的同步控制。以下是改进版本:
package main
import (
"fmt"
"sync"
"time"
)
type Post struct {
ID int
Text string
}
func main() {
var posts []Post
var wg sync.WaitGroup
p1 := CreatePost(1, "yahoo")
p2 := CreatePost(2, "google 1")
p3 := CreatePost(3, "excite 2")
p4 := CreatePost(4, "altavista 3")
p5 := CreatePost(5, "bing 4")
posts = append(posts, p1, p2, p3, p4, p5)
// 创建带缓冲的通道
postCh := make(chan Post, 5)
// 启动生产者goroutine
wg.Add(1)
go ProducePosts(postCh, posts, &wg)
// 启动消费者goroutine
wg.Add(1)
go ConsumePosts(postCh, &wg)
// 等待所有goroutine完成
wg.Wait()
fmt.Println("所有处理完成")
}
// 生产者函数 - 向通道发送数据
func ProducePosts(c chan<- Post, po []Post, wg *sync.WaitGroup) {
defer wg.Done()
defer close(c) // 确保通道被关闭
for _, v := range po {
// 模拟处理时间
time.Sleep(100 * time.Millisecond)
c <- v
fmt.Printf("生产: %v\n", v)
}
}
// 消费者函数 - 从通道接收数据
func ConsumePosts(c <-chan Post, wg *sync.WaitGroup) {
defer wg.Done()
for v := range c {
// 模拟处理时间
time.Sleep(50 * time.Millisecond)
fmt.Printf("消费: ID=%d, Text=%s\n", v.ID, v.Text)
}
}
func CreatePost(id int, text string) Post {
return Post{
ID: id,
Text: text,
}
}
更复杂的并发处理示例
// 使用多个生产者和消费者的示例
func main() {
var wg sync.WaitGroup
postCh := make(chan Post, 10)
// 创建测试数据
posts := []Post{
CreatePost(1, "Post 1"),
CreatePost(2, "Post 2"),
CreatePost(3, "Post 3"),
CreatePost(4, "Post 4"),
CreatePost(5, "Post 5"),
}
// 启动2个生产者
wg.Add(2)
go func() {
defer wg.Done()
for i := 0; i < 3; i++ {
postCh <- posts[i]
}
}()
go func() {
defer wg.Done()
for i := 3; i < 5; i++ {
postCh <- posts[i]
}
}()
// 启动3个消费者
wg.Add(3)
for i := 0; i < 3; i++ {
go func(workerID int) {
defer wg.Done()
for post := range postCh {
fmt.Printf("Worker %d 处理: %v\n", workerID, post)
}
}(i)
}
// 等待生产者完成并关闭通道
go func() {
wg.Wait()
// 注意:这里需要小心处理wg的状态
close(postCh)
}()
// 等待所有消费者完成
wg.Wait()
}
关键要点
- 通道方向: 使用
chan<-和<-chan明确通道的读写方向 - 等待组传递: 等待组必须通过指针传递
&wg - 资源清理: 使用
defer close(c)确保通道被正确关闭 - 缓冲选择: 根据数据量大小合理设置通道缓冲区大小
这种模式确保了数据的正确传递和goroutine的同步执行,避免了数据竞争和资源泄漏。

