package main
import (
"fmt"
"sync"
"time"
)
// ProgressBar 结构体用于管理进度状态
type ProgressBar struct {
current int
total int
mu sync.Mutex // 互斥锁,确保线程安全
}
// NewProgressBar 初始化一个新的进度条
func NewProgressBar(total int) *ProgressBar {
return &ProgressBar{
current: 0,
total: total,
}
}
// Update 更新进度并显示
func (pb *ProgressBar) Update() {
pb.mu.Lock()
pb.current++
percent := float64(pb.current) / float64(pb.total) * 100
pb.mu.Unlock()
barLength := 20 // 进度条长度
filled := int(float64(barLength) * float64(pb.current) / float64(pb.total))
// 构建进度条字符串
bar := "["
for i := 0; i < barLength; i++ {
if i < filled {
bar += "="
} else {
bar += " "
}
}
bar += "]"
// 使用 \r 将光标移到行首,覆盖前一行输出
fmt.Printf("\r任务进度: %.1f%% %s (%d/%d)", percent, bar, pb.current, pb.total)
}
// 模拟后台任务
func backgroundTask(pb *ProgressBar, done chan struct{}) {
for i := 0; i < pb.total; i++ {
time.Sleep(100 * time.Millisecond) // 模拟任务耗时
pb.Update()
}
close(done) // 任务完成,关闭通道
}
func main() {
totalSteps := 100 // 总步数
pb := NewProgressBar(totalSteps)
done := make(chan struct{}) // 用于通知任务完成
fmt.Println("开始执行后台任务...")
go backgroundTask(pb, done) // 在 goroutine 中运行后台任务
// 等待任务完成
<-done
// 任务完成,换行并打印完成信息
fmt.Printf("\n任务完成!\n")
}
关键点:
- 加锁(sync.Mutex):
- 在
ProgressBar
中添加 mu sync.Mutex
,在 Update
方法中用 pb.mu.Lock()
和 pb.mu.Unlock()
保护 current
的读写,避免数据竞争。
- 通道(chan)代替轮询:
- 添加一个
done
通道,backgroundTask
在完成后关闭它,主线程通过 <-done
等待任务结束,避免忙等待。
- 简化逻辑:
backgroundTask
现在直接用循环控制进度到 total
,不需要检查 pb.current < pb.total
。
- 输出刷新:
- 使用
\r
确保每次进度更新覆盖同一行,终端会正确显示。