繁体   English   中英

如何使这个 golang for-select 代码工作?

[英]How to make this golang for-select code work?

问题

如何使以下代码在 3 秒后打印“QUIT”?

代码

package main

import (
    "fmt"
    "time"
)

func main() {
    quit := make(chan struct{})
    tasks := make(chan struct{})

    go func(){
      time.Sleep(1 * time.Second)
      tasks <- struct{}{}
    }()

    go func(){
      time.Sleep(3 * time.Second)
      quit <- struct{}{}
    }()

    for {
        select {
        case <-quit:
            fmt.Println("QUIT")
            return
        case <-tasks:
            fmt.Println("Doing")
            // do some long time jobs more than 10 seconds
            time.Sleep(10 * time.Second)
        }
    }
}

观察

上面的代码打印“Doing”。 并休眠 10 秒,然后打印“QUIT”。 如何中断此睡眠,让它在 3 秒后接收quit频道并打印“QUIT”?

似乎selectcase tasks阻塞,并且在 3 秒后它不会从quit通道接收。

要发出异步任务结束的信号,最好关闭通道,这对于防止许多导致各种死锁的误用非常重要。 在您的原始代码中,我会写close(quite)而不是quit <- struct{}{}请记住,在关闭时读取不会阻塞并始终返回零值,这就是诀窍。

无论如何,除此之外,解决问题的一种优雅方法是结合使用context.Contexttime.After

time.After将帮助您阻止可选择的任务集。

context.Context更适合处理这种信号。

https://play.golang.org/p/ZVsZw3P-YHd

package main

import (
    "context"
    "log"
    "time"
)

func main() {
    log.Println("start")
    defer log.Println("end")
    ctx, cancel := context.WithCancel(context.Background())
    tasks := make(chan struct{})

    go func() {
        time.Sleep(1 * time.Second) // a job
        tasks <- struct{}{}
    }()

    go func() {
        time.Sleep(3 * time.Second) // a job
        cancel()
    }()

    for {
        select {
        case <-ctx.Done():
            log.Println("QUIT")
            return
        case <-tasks:
            log.Println("Doing")
            // do some long time jobs more than 10 seconds
            select {
            case <-ctx.Done():
                return
            case <-time.After(time.Second * 10):
            }
        }
    }
}

第二个作业运行了 10 秒,所以它会阻塞这段时间的循环,并且在这个作业完成之前不会收到你发送到quit通道的信号。

为了打断这个耗时的工作,也许你可以把它拆分成另一个 goroutine。 这样的事情会做:

go func() {
  for {
    select {
    case <-tasks:
      fmt.Println("Doing")
      // do some long time jobs more than 10 seconds
      time.Sleep(10 * time.Second)
    }   
  }   
}() 

<-quit
fmt.Println("QUIT")

使用 [sync.WaitGroup.

package main

import (
    "fmt"
    "sync"
    "time"
)

func worker(msg string, duration time.Duration, doing bool, wg *sync.WaitGroup) {
    defer wg.Done()
    time.Sleep(duration)

    fmt.Println(msg)

    if doing {
        time.Sleep(10 * time.Second)
    }
}

func main() {

    var wg sync.WaitGroup
    msgs := [2]string{"QUIT", "Doing"}
    durations := [2]time.Duration{3 * time.Second, 1 * time.Second}
    doing := [2]bool{false, true}

    for i, msg := range msgs {
        wg.Add(1)

        go worker(msg, durations[i], doing[i], &wg)
    }

    wg.Wait()
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM