【问题标题】:Why does the goroutine not block for time.Sleep(duration)为什么 goroutine 不阻塞 time.Sleep(duration)
【发布时间】:2021-12-09 15:32:11
【问题描述】:

我运行的程序:

package main

import (
    "flag"
    "fmt"
    "os"
    "os/signal"
    "time"
)

var sleepSeconds string
var timeToSleep time.Duration

func init() {
    flag.StringVar(&sleepSeconds, "sleep", "1s", "-sleep \"3s\"")
}

func main() {
    startTime := time.Now()
    fmt.Println("Start Time: ", startTime)
    c := make(chan os.Signal)
    signal.Notify(c, os.Interrupt)

    // Block until a signal is received.
    flag.Parse()
    timeToSleep, err := time.ParseDuration(sleepSeconds)
    if err != nil {
        fmt.Println("unable to parse the duration: ", err)
        return
    }
    fmt.Println("Time to Sleep: ", timeToSleep)

    myChan := make(chan string)
    go one(myChan)
    go two(myChan)
    go three(myChan)
    var s os.Signal
    fmt.Println("Signal is, ", s)
    go func() {
        s = <-c
        fmt.Println("After receiving Signal is, ", s)

    }()
    i := 0
    for {
        i++
        generatedString := fmt.Sprintf("%d", i)
        fmt.Println("Generating... ", generatedString)
        myChan <- generatedString
        //      time.Sleep(timeToSleep)
        fmt.Println("Length of Channel: ", len(myChan))
        if s != nil {
            break
        }
    }
    fmt.Println("Total time the program ran for: ", time.Since(startTime))
}

func one(dataCh chan string) {
    for val := range dataCh {
        fmt.Printf("Value received in func one: %s\n", val)
        time.Sleep(timeToSleep)
    }
}

func two(dataCh chan string) {
    for val := range dataCh {
        fmt.Printf("Value received in func two: %s\n", val)
        time.Sleep(timeToSleep)
    }
}

func three(dataCh chan string) {
    for val := range dataCh {
        fmt.Printf("Value received in func three: %s\n", val)
        time.Sleep(timeToSleep)
    }
}

当我使用以下标志 (5s) 运行此程序时

./ichan -sleep "5s" &gt; data.txt

输出令人费解,因为我看到程序只运行了2.31606525s,但所有 3 个 goroutine 都应该被阻止至少 5 秒。 data.txt 是这个repository 的一部分供参考,代码也可以在同一个存储库中找到。

我的问题是:one、two 和 three 函数中的 time.Sleep() 应该被阻塞直到睡眠持续时间,然后消耗来自channel,但观察结果是 sleep 对 goroutine 没有任何影响。

【问题讨论】:

  • 您在没有同步的情况下从多个 goroutine(一个写入它)访问 s 变量:这是一个数据竞争,因此是未定义的行为。
  • @icza 即使没有s,行为也是一样的
  • 如果相同,请提供一个没有数据竞争的示例,以便我们专注于真正的问题。
  • 你怎么知道函数中的time.Sleep()没有阻塞效果?你观察到了什么?
  • 请检查存储库中的 data.txt 文件。如果睡眠有效,则不应使用此值打印Value received in func two: 181191,它应该是小得多的数字。但是,根据@icza 请求,我对源代码进行了更改,并使用新分支no_signals_final 更新了存储库注意:我看到了相同的行为

标签: go channel


【解决方案1】:

timeToSleep, err := 创建一个名为 timeToSleep 的新局部变量,它独立于同名的包级变量,因此包级变量始终为零。

您似乎错过了原生持续时间标志类型:https://pkg.go.dev/flag#DurationVar

func init() {
    flag.DurationVar(&timeToSleep, "sleep", 1*time.Second, `-sleep "3s"`)
}

【讨论】:

  • 感谢您的回答,我确认问题和解决方案并且它有效
  • 我也确实错过了标志包可以选择接受标志作为持续时间
【解决方案2】:

同一程序的工作副本

package main

import (
    "flag"
    "fmt"
    "os"
    "os/signal"
    "time"
)

var sleepSeconds string
var timeToSleep time.Duration
var err error

func init() {
    flag.StringVar(&sleepSeconds, "sleep", "1s", "-sleep \"3s\"")
}

func main() {
    startTime := time.Now()
    fmt.Println("Start Time: ", startTime)
    c := make(chan os.Signal)
    signal.Notify(c, os.Interrupt)

    // Block until a signal is received.
    flag.Parse()
    timeToSleep, err = time.ParseDuration(sleepSeconds)
    if err != nil {
        fmt.Println("unable to parse the duration: ", err)
        return
    }
    fmt.Println("Time to Sleep: ", timeToSleep)

    myChan := make(chan string)
    go one(myChan)
    go two(myChan)
    go three(myChan)
    var s os.Signal
    fmt.Println("Signal is, ", s)
    go func() {
        s = <-c
        fmt.Println("After receiving Signal is, ", s)

    }()
    i := 0
    for {
        i++
        generatedString := fmt.Sprintf("%d", i)
        fmt.Println("Generating... ", generatedString)
        myChan <- generatedString
        //      time.Sleep(timeToSleep)
        fmt.Println("Length of Channel: ", len(myChan))
        if s != nil {
            break
        }
    }
    fmt.Println("Total time the program ran for: ", time.Since(startTime))
}

func one(dataCh chan string) {
    for val := range dataCh {
        fmt.Printf("Value received in func one: %s\n", val)
        time.Sleep(timeToSleep)
    }
}

func two(dataCh chan string) {
    for val := range dataCh {
        fmt.Printf("Value received in func two: %s\n", val)
        time.Sleep(timeToSleep)
    }
}

func three(dataCh chan string) {
    for val := range dataCh {
        fmt.Printf("Value received in func three: %s\n", val)
        time.Sleep(timeToSleep)
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-07-14
    • 2013-03-24
    • 1970-01-01
    • 1970-01-01
    • 2012-10-19
    • 1970-01-01
    • 2021-05-19
    • 1970-01-01
    相关资源
    最近更新 更多