为什么 goroutine 在执行 time.Sleep(duration) 时不会阻塞?

huangapple go评论82阅读模式
英文:

Why does the goroutine not block for time.Sleep(duration)

问题

我运行的程序:

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("开始时间:", startTime)
	c := make(chan os.Signal)
	signal.Notify(c, os.Interrupt)

	// 直到接收到信号为止阻塞程序。
	flag.Parse()
	timeToSleep, err := time.ParseDuration(sleepSeconds)
	if err != nil {
		fmt.Println("无法解析持续时间:", err)
		return
	}
	fmt.Println("睡眠时间:", timeToSleep)

	myChan := make(chan string)
	go one(myChan)
	go two(myChan)
	go three(myChan)
	var s os.Signal
	fmt.Println("信号是:", s)
	go func() {
		s = <-c
		fmt.Println("接收到信号后的信号是:", s)

	}()
	i := 0
	for {
		i++
		generatedString := fmt.Sprintf("%d", i)
		fmt.Println("生成中... ", generatedString)
		myChan <- generatedString
		// time.Sleep(timeToSleep)
		fmt.Println("通道长度:", len(myChan))
		if s != nil {
			break
		}
	}
	fmt.Println("程序运行的总时间:", time.Since(startTime))
}

func one(dataCh chan string) {
	for val := range dataCh {
		fmt.Printf("在函数 one 中接收到的值:%s\n", val)
		time.Sleep(timeToSleep)
	}
}

func two(dataCh chan string) {
	for val := range dataCh {
		fmt.Printf("在函数 two 中接收到的值:%s\n", val)
		time.Sleep(timeToSleep)
	}
}

func three(dataCh chan string) {
	for val := range dataCh {
		fmt.Printf("在函数 three 中接收到的值:%s\n", val)
		time.Sleep(timeToSleep)
	}
}

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

./ichan -sleep "5s" > data.txt

输出令人困惑,因为我看到程序只运行了 2.31606525s,但是所有 3 个 goroutine 应该至少被阻塞 5 秒钟。
data.txt 是此存储库的一部分,供参考,代码也在同一存储库中可用。

我的问题是: 函数 onetwothree 中的 time.Sleep() 应该被阻塞,直到睡眠时间结束,然后从通道中消耗字符串,但观察结果是睡眠对 goroutine 没有任何影响。

英文:

The program that I ran:

package main

import (
	&quot;flag&quot;
	&quot;fmt&quot;
	&quot;os&quot;
	&quot;os/signal&quot;
	&quot;time&quot;
)

var sleepSeconds string
var timeToSleep time.Duration

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

func main() {
	startTime := time.Now()
	fmt.Println(&quot;Start Time: &quot;, 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(&quot;unable to parse the duration: &quot;, err)
		return
	}
	fmt.Println(&quot;Time to Sleep: &quot;, timeToSleep)

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

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

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

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

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

When I run this program with the following flags(5s)

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

output is puzzling as I see the program has only run for 2.31606525s but all 3 goroutines are supposed to be blocked at least for 5 seconds each.
data.txt is part of this repository for reference, code is also available in the same repository.

My Question is: time.Sleep() in the functions one, two and three are supposed to be blocked until the duration of sleep and then consume the strings from the channel, but the observation is that sleep is not having any effect on the goroutines.

答案1

得分: 2

timeToSleep, err := 创建了一个新的局部变量 timeToSleep,它与同名的包级变量是独立的,所以包级变量始终为零。

看起来你可能忽略了存在一个本地的持续时间标志类型:https://pkg.go.dev/flag#DurationVar

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

timeToSleep, err := creates a new, local variable named timeToSleep that is independent of the package level variable with the same name, so the package level variable is always zero.

It seems you have missed that there is a native duration flag type: https://pkg.go.dev/flag#DurationVar

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

答案2

得分: 0

相同程序的工作副本

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("开始时间:", startTime)
	c := make(chan os.Signal)
	signal.Notify(c, os.Interrupt)

	// 阻塞直到接收到信号。
	flag.Parse()
	timeToSleep, err = time.ParseDuration(sleepSeconds)
	if err != nil {
		fmt.Println("无法解析持续时间:", err)
		return
	}
	fmt.Println("睡眠时间:", timeToSleep)

	myChan := make(chan string)
	go one(myChan)
	go two(myChan)
	go three(myChan)
	var s os.Signal
	fmt.Println("信号是:", s)
	go func() {
		s = <-c
		fmt.Println("接收到信号后的信号是:", s)

	}()
	i := 0
	for {
		i++
		generatedString := fmt.Sprintf("%d", i)
		fmt.Println("生成中... ", generatedString)
		myChan <- generatedString
		// time.Sleep(timeToSleep)
		fmt.Println("通道长度:", len(myChan))
		if s != nil {
			break
		}
	}
	fmt.Println("程序运行的总时间:", time.Since(startTime))
}

func one(dataCh chan string) {
	for val := range dataCh {
		fmt.Printf("在函数 one 中接收到的值:%s\n", val)
		time.Sleep(timeToSleep)
	}
}

func two(dataCh chan string) {
	for val := range dataCh {
		fmt.Printf("在函数 two 中接收到的值:%s\n", val)
		time.Sleep(timeToSleep)
	}
}

func three(dataCh chan string) {
	for val := range dataCh {
		fmt.Printf("在函数 three 中接收到的值:%s\n", val)
		time.Sleep(timeToSleep)
	}
}

英文:

Working Copy of the same program

package main

import (
	&quot;flag&quot;
	&quot;fmt&quot;
	&quot;os&quot;
	&quot;os/signal&quot;
	&quot;time&quot;
)

var sleepSeconds string
var timeToSleep time.Duration
var err error

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

func main() {
	startTime := time.Now()
	fmt.Println(&quot;Start Time: &quot;, 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(&quot;unable to parse the duration: &quot;, err)
		return
	}
	fmt.Println(&quot;Time to Sleep: &quot;, timeToSleep)

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

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

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

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

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

huangapple
  • 本文由 发表于 2021年10月23日 15:55:50
  • 转载请务必保留本文链接:https://go.coder-hub.com/69686181.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定