Golang中并发问题的疑问与讨论
Golang中并发问题的疑问与讨论 我不理解为什么以下代码会先输出大写字母,然后是小写字母,而在主文件中打印小写字母的goroutine先于打印大写字母的goroutine?
代码如下:
package main
import (
"fmt"
"sync"
)
func main(){
var wg = sync.WaitGroup{}
wg.Add(2)
go func(){
defer wg.Done()
printAlpha("lower")
}()
go func(){
defer wg.Done()
printAlpha("upper")
}()
wg.Wait()
fmt.Println("Done")
}
func printAlpha(sign string){
if sign == "lower"{
for char := 'a'; char < 'a' + 26; char++{
fmt.Printf("%c",char)
}
}else{
for char := 'A'; char < 'A' + 26; char++{
fmt.Printf("%c",char)
}
}
}
输出结果:
ABCDEFGHIJKLabcdefghijklmnopqrMNOPQRSTUVWXYZstuvwxyzDone
更多关于Golang中并发问题的疑问与讨论的实战教程也可以访问 https://www.itying.com/category-94-b0.html
实际上这是不可预测的。你在屏幕上看到的是所有3个goroutine的输出混乱地混杂在一起。
如果你仔细观察,应该会注意到屏幕输出存在数据流"管理"缺失的问题。
场景 当前情况就像一支队伍在进行100码赛跑。所有人同时起跑,但很难预测谁在第4秒(t=4s)时以8.3英里/小时的速度奔跑,以及哪条跑道在每场比赛中最先到达终点。
在你的main函数中,你委托了2个进程分别对同一输出执行printAlpha。一旦所有3个goroutine(main、“lower” goroutine和"upper" goroutine)启动,虽然启动时间明确显示main是第一,"lower"是第二,"upper"是第三,但很难说哪个先运行,哪个在哪个时刻执行。另外请记住,启动新的goroutine并不能保证该goroutine会立即被调度。
这是我在本地机器上使用基准测试工具运行你的函数时的截图。注意每行内容都不一致。高亮显示的是小写字母先运行的情况。

关于goroutine的技术细节,Dave在他的博客中写了一篇很好的技术文章:没有事件循环的性能表现 | Dave Cheney。
该怎么做?
清晰地规划你的并发逻辑,比如谁应该在什么时候向输出写入什么内容。然后,你可以确保每个goroutine以同步的方式协同工作。
回到100码赛跑的比喻 - 在终点线设置收费亭,然后规定谁和谁应该先进入。这样,你就能保证每条跑道的输出结果。
在Go语言中,goroutine的执行顺序是不确定的,由Go运行时调度器决定。代码中先启动打印小写字母的goroutine,后启动打印大写字母的goroutine,但实际执行顺序可能相反,这取决于调度器的决策。
此外,fmt.Printf函数内部涉及I/O操作,可能导致输出交错。虽然每个goroutine内部循环是顺序的,但多个goroutine并发执行时,输出可能混合。在您的输出中,大写字母先开始打印,但被小写字母中断,最后又继续大写字母,这表明两个goroutine在并发执行。
以下是修改后的代码,使用互斥锁确保每个goroutine完整输出字母表,避免交错:
package main
import (
"fmt"
"sync"
)
var mu sync.Mutex
func main() {
var wg sync.WaitGroup
wg.Add(2)
go func() {
defer wg.Done()
printAlpha("lower")
}()
go func() {
defer wg.Done()
printAlpha("upper")
}()
wg.Wait()
fmt.Println("Done")
}
func printAlpha(sign string) {
mu.Lock()
defer mu.Unlock()
if sign == "lower" {
for char := 'a'; char < 'a'+26; char++ {
fmt.Printf("%c", char)
}
} else {
for char := 'A'; char < 'A'+26; char++ {
fmt.Printf("%c", char)
}
}
}
此代码使用sync.Mutex确保每次只有一个goroutine执行printAlpha函数,输出将是完整的字母序列(如先全部大写后全部小写,或反之),但顺序仍不确定。例如,可能输出ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzDone或相反。

