Golang并发打印字母表的最佳实践
Golang并发打印字母表的最佳实践 我需要帮助解决一个任务:Go Playground
在函数 pool() 中,我创建了一个池并用ID(整数值)填充它。
之后我初始化了两个函数:
worker:从池中获取id并并发运行打印字母的函数。最后我将id返回给池waitDone:等待所有id返回
我在解决这个任务时卡住了。我需要在不使用 time.Sleep 和 sync 包的情况下并发打印所有字母。
死锁通常发生在一个 goroutine 无法向通道写入数据,因为另一个 goroutine 尚未准备好读取。
您可以采取以下措施:
- 在每一个发生有趣事件的地方放置
log.Println()语句,例如:向通道发送数据时、函数开始或结束时等。判断哪些代码行足够重要,值得在其前后放置 Println 语句。 - 然后观察程序在死锁前打印了什么。
输出结果可以帮助缩小死锁原因的范围。
我需要使用大量 goroutine 来打印字母表。示例中有两个 worker(即两个 goroutine),但代码应该能适用于 4、10 甚至 1000 个 goroutine,并且我们打印的文本本身也可能更长。
由于 goroutine 的特性,确切的输出可能会有所不同,但它最终应该能打印出完整的文本。
让这段代码并发的意义是什么? 为什么需要池和 worker?
因为这是我的任务 =)
那你具体卡在哪里了?
我在池函数中遇到了死锁。
当我在Playground中运行你的代码时,并没有遇到死锁,它只是直接退出,没有打印任何内容。我认为这是因为你的worker函数在一个goroutine中启动了f,但随后立即将i返回给了pool,导致你稍后从main启动的waitDone goroutine能够从池中取出所有内容,并在f goroutine开始之前就退出了:
worker := func(l string) {
i := <-pool
go f(i, l)
pool <- i
}
我认为你应该在f执行完成之后,才将i返回到pool。
package main
import (
"fmt"
)
func pool() {
// 创建字母池
letters := make(chan rune, 26)
// 填充字母池
for i := 'a'; i <= 'z'; i++ {
letters <- i
}
close(letters)
// 创建完成信号通道
done := make(chan bool)
// 启动worker
worker := func() {
for letter := range letters {
fmt.Printf("%c ", letter)
}
done <- true
}
// 启动多个worker并发处理
for i := 0; i < 5; i++ { // 使用5个worker并发处理
go worker()
}
// 等待所有worker完成
for i := 0; i < 5; i++ {
<-done
}
}
func main() {
pool()
}
输出示例(顺序可能不同,因为是并发执行):
a b c d e f g h i j k l m n o p q r s t u v w x y z
更简洁的版本,使用goroutine和channel同步:
package main
import (
"fmt"
)
func pool() {
// 创建任务通道
tasks := make(chan rune)
// 启动worker
worker := func(id int) {
for letter := range tasks {
fmt.Printf("Worker %d: %c\n", id, letter)
}
}
// 启动3个worker
for i := 1; i <= 3; i++ {
go worker(i)
}
// 发送任务
for i := 'a'; i <= 'z'; i++ {
tasks <- i
}
close(tasks)
}
func main() {
pool()
}
使用缓冲通道控制并发数量的版本:
package main
import (
"fmt"
)
func pool() {
// 创建字母通道
letters := make(chan rune)
done := make(chan bool)
// worker函数
worker := func(id int) {
for letter := range letters {
fmt.Printf("%c ", letter)
}
done <- true
}
// 启动固定数量的worker
workerCount := 4
for i := 0; i < workerCount; i++ {
go worker(i)
}
// 发送所有字母
go func() {
for i := 'a'; i <= 'z'; i++ {
letters <- i
}
close(letters)
}()
// 等待所有worker完成
for i := 0; i < workerCount; i++ {
<-done
}
}
func main() {
pool()
}
这些实现都符合要求:
- 完全并发打印字母表
- 不使用
time.Sleep - 不使用
sync包 - 通过channel实现同步和通信


