Golang 并发的退出

《The Go Programming Language》笔记

关闭一个channel,操作channel之后的代码可以立即被执行,并且会产生零值。
广播机制:用关闭一个channel来进行广播。创建一个退出channel,不会向这个channel发送任何值。
import (
    "flag"
    "fmt"
    "io/ioutil"
    "os"
    "path/filepath"
    "sync"
    "time"
)

//一个已经被关闭的channel不会阻塞,已经被关闭的channel会实时返回
//goroutine退出,关闭done来进行广播
var done = make(chan struct{})

//判断done是否关闭,即是否执行goroutine退出
func cancelled() bool {
    select {
    case <-done:
        return true
    default:
        return false
    }
}

//获取目录dir下的文件大小
func walkDir(dir string, wg *sync.WaitGroup, fileSizes chan<- int64) {
    defer wg.Done()
    if cancelled() {
        return
    }
    for _, entry := range dirents(dir) {
        if entry.IsDir() { //目录
            wg.Add(1)
            subDir := filepath.Join(dir, entry.Name())
            go walkDir(subDir, wg, fileSizes)
        } else {
            fileSizes <- entry.Size()
        }
    }
}

//sema is a counting semaphore for limiting concurrency in dirents
var sema = make(chan struct{}, 20)

//读取目录dir下的文件信息
func dirents(dir string) []os.FileInfo {
    select {
    case sema <- struct{}{}: //acquire token
    case <-done:
        return  nil // cancelled
    }
    defer func() { <-sema }() //release token
    entries, err := ioutil.ReadDir(dir)
    if err != nil {
        fmt.Fprintf(os.Stderr, "du: %v\n", err)
        return nil
    }
    return entries
}

//输出文件数量的大小
func printDiskUsage(nfiles, nbytes int64) {
    fmt.Printf("%d files %.1f GB\n", nfiles, float64(nbytes)/1e9)
}

//提供-v 参数会显示程序进度信息
var verbose = flag.Bool("v", false, "show verbose progress messages")

func Start() {
    flag.Parse()
    roots := flag.Args() //需要统计的目录
    if len(roots) == 0 {
        roots = []string{"."}
    }
    fileSizes := make(chan int64, 5)
    var wg sync.WaitGroup
    for _, root := range roots {
        wg.Add(1)
        go walkDir(root, &wg, fileSizes)
    }
    go func() {
        os.Stdin.Read(make([]byte, 1)) //从标准输入读取一个字符,执行goroutine退出
        close(done)
    }()
    go func() {
        wg.Wait() //等待goroutine结束
        close(fileSizes)
    }()
    var tick <-chan time.Time
    if *verbose {
        tick = time.Tick(100 * time.Millisecond) //输出时间间隔
    }
    var nfiles, nbytes int64
loop:
    for {
        select {
        case <-done:
                //to allow existing goroutines to finish
            for range fileSizes { //fileSizes关闭时,for循环会自动结束
                //Do nothing
            }
            return
        case size, ok := <-fileSizes:
            if !ok {
                break loop
            }
            nfiles++
            nbytes += size
        case <-tick:
            printDiskUsage(nfiles, nbytes)
        }
    }
    printDiskUsage(nfiles, nbytes)
}
### Golang 并发编程练习示例代码 #### 使用 `sync.WaitGroup` 实现并发任务等待完成 下面展示了一个使用 `sync.WaitGroup` 的简单例子,该程序启动多个 Goroutine 来模拟并发任务并等待它们全部完成。 ```go package main import ( "fmt" "sync" ) func worker(id int, wg *sync.WaitGroup) { defer wg.Done() fmt.Printf("Worker %d starting\n", id) // Simulate some work with a sleep. for i := 0; i < 3; i++ { fmt.Printf("Worker %d is working...\n", id) } fmt.Printf("Worker %d done\n", id) } func main() { var wg sync.WaitGroup numWorkers := 5 for i := 1; i <= numWorkers; i++ { wg.Add(1) go worker(i, &wg) } wg.Wait() fmt.Println("All workers have finished their tasks.") } ``` 此代码展示了如何通过 `WaitGroup` 控制多个 Goroutine 的生命周期[^4]。 --- #### 使用 Channel 进行消息传递的任务队列改造 基于引用中的计时器退出逻辑[^3],可以将其改造成一个简单的任务队列模型。以下是一个改进版本: ```go package main import ( "fmt" "time" ) type Task struct { Name string Duration time.Duration } var taskQueue chan Task var exitChan chan bool func processTasks(ticker *time.Ticker) { for { select { case t := <-taskQueue: fmt.Printf("Processing task: %s\n", t.Name) time.Sleep(t.Duration) fmt.Printf("Task completed: %s\n", t.Name) case <-ticker.C: fmt.Println("Checking for new tasks...") case <-exitChan: fmt.Println("Exiting the processor goroutine.") return } } } func main() { taskQueue = make(chan Task, 10) exitChan = make(chan bool, 1) ticker := time.NewTicker(time.Second * 2) go processTasks(ticker) // Add tasks to the queue for i := 1; i <= 5; i++ { task := Task{Name: fmt.Sprintf("Task-%d", i), Duration: time.Second} taskQueue <- task if i == 3 { close(taskQueue) // Close channel after adding all tasks (optional). } } // Exit signal after processing all tasks time.Sleep(time.Second * 8) exitChan <- true } ``` 上述代码创建了一个任务队列,并利用通道来管理任务的分发和处理过程。 --- #### 使用 Select 和 Timeout 处理超时场景 以下代码演示了如何在 Go 中使用 `select` 和 `time.After` 函数实现超时控制[^2]。 ```go package main import ( "fmt" "time" ) func fetchData(ch chan<- string) { time.Sleep(3 * time.Second) // Simulating network delay or computation ch <- "Data fetched successfully!" } func main() { dataCh := make(chan string, 1) timeoutCh := time.After(2 * time.Second) go fetchData(dataCh) select { case data := <-dataCh: fmt.Println("Received:", data) case <-timeoutCh: fmt.Println("Request timed out!") } } ``` 在此示例中,如果数据获取超过指定的时间,则会触发超时机制。 --- #### 使用 Mutex 和 Condition 变量同步访问共享资源 以下代码展示了如何使用 `sync.Mutex` 或 `sync.Cond` 同步对共享变量的访问。 ```go package main import ( "fmt" "sync" ) func main() { var mu sync.Mutex count := 0 increment := func(wg *sync.WaitGroup) { mu.Lock() defer mu.Unlock() count++ fmt.Println("Count incremented:", count) wg.Done() } var wg sync.WaitGroup for i := 0; i < 5; i++ { wg.Add(1) go increment(&wg) } wg.Wait() fmt.Println("Final Count:", count) } ``` 这段代码说明了如何防止竞争条件的发生,从而确保线程安全的操作。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值