Scott

golang版终端进度条 4 months ago

go
1705个字符
共有38人围观

博客大纲

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")
}

关键点:

  1. 加锁(sync.Mutex)
    • ProgressBar 中添加 mu sync.Mutex,在 Update 方法中用 pb.mu.Lock()pb.mu.Unlock() 保护 current 的读写,避免数据竞争。
  2. 通道(chan)代替轮询
    • 添加一个 done 通道,backgroundTask 在完成后关闭它,主线程通过 <-done 等待任务结束,避免忙等待。
  3. 简化逻辑
    • backgroundTask 现在直接用循环控制进度到 total,不需要检查 pb.current < pb.total
  4. 输出刷新
    • 使用 \r 确保每次进度更新覆盖同一行,终端会正确显示。