需要帮助理解缓冲通道。

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

Require help in understanding buffered channels

问题

我很难理解缓冲通道的工作原理。根据以下示例,我正在尝试同时利用两个线程打印出当前时间,每两个go调用之间大约延迟2秒:

package main
import "fmt"
import "time"

func main() {
    returnCurrentTime := func() string  {
        return time.Now().String()
    }

    c := make(chan string, 2)

    asyncReturnCurrentTime := func(c chan string) {
        time.Sleep(2001 * time.Millisecond)
        c <- returnCurrentTime()
    }

    for i := 1; i != 7; i++ {
        go asyncReturnCurrentTime(c)
        if(i % 3 == 0) {
            fmt.Println(<- c)
            fmt.Println(<- c)
            fmt.Println(<- c)
            fmt.Println()
        }
    }
}

这将产生以下结果:

2013-02-27 03:17:50
2013-02-27 03:17:50
2013-02-27 03:17:50

2013-02-27 03:17:52
2013-02-27 03:17:52
2013-02-27 03:17:52

我对秒数的期望是每两个go调用之间延迟2秒,在这种情况下,期望的结果如下:

2013-02-27 03:17:50
2013-02-27 03:17:50
2013-02-27 03:17:52 <- 第3次调用,有2个缓冲槽

2013-02-27 03:17:54
2013-02-27 03:17:54
2013-02-27 03:17:56 <- 第3次调用,有2个缓冲槽

显然,我误解了缓冲通道的概念,请问有人能够友好地解释我的逻辑错误以及如何实现预期的结果吗?

谢谢

英文:

I'm having a hard time understanding how buffered channels work. Based on the following example, i'm trying to utilize 2 threads at a time to print out the current time, with approximately 2 second delays between every 2 go calls:

package main
import &quot;fmt&quot;
import &quot;time&quot;

func main() {
	returnCurrentTime := func() string  {
		return time.Now().String()
	}

	c := make(chan string, 2)

	asyncReturnCurrentTime := func(c chan string) {
		time.Sleep(2001 * time.Millisecond)
		c &lt;- returnCurrentTime()
	}

	for i := 1; i != 7; i++ {
		go asyncReturnCurrentTime(c)
		if(i % 3 == 0) {
			fmt.Println(&lt;- c)
			fmt.Println(&lt;- c)
			fmt.Println(&lt;- c)
			fmt.Println()
		}
	}
}

This produces

2013-02-27 03:17:50
2013-02-27 03:17:50
2013-02-27 03:17:50

2013-02-27 03:17:52
2013-02-27 03:17:52
2013-02-27 03:17:52

What i'm expecting regarding the seconds is 2 second delays between ever 2 go calls and in this case the following result

2013-02-27 03:17:50
2013-02-27 03:17:50
2013-02-27 03:17:52 &lt;- 3rd call with 2 buffer slots

2013-02-27 03:17:54
2013-02-27 03:17:54
2013-02-27 03:17:56 &lt;- 3rd call with 2 buffer slots

Obviously i misunderstood the concept of buffered channels, would somebody please be kind enough to explain the fault in my logic and how to achieve the expected result?

Thank you

答案1

得分: 3

你正在运行以下代码:

package main

import (
	"fmt"
	"time"
)

func main() {
	returnCurrentTime := func() string {
		return time.Now().String()
	}

	c := make(chan string, 2)

	asyncReturnCurrentTime := func(c chan string) {
		time.Sleep(2001 * time.Millisecond)
		c <- returnCurrentTime()
	}

	go asyncReturnCurrentTime(c)
	go asyncReturnCurrentTime(c)
	go asyncReturnCurrentTime(c)
	fmt.Println(<-c)
	fmt.Println(<-c)
	fmt.Println(<-c)
	fmt.Println()
	go asyncReturnCurrentTime(c)
	go asyncReturnCurrentTime(c)
	go asyncReturnCurrentTime(c)
	fmt.Println(<-c)
	fmt.Println(<-c)
	fmt.Println(<-c)
	fmt.Println()
}

输出结果:

2013-02-26 21:28:22.069581655 -0500 EST
2013-02-26 21:28:22.069688722 -0500 EST
2013-02-26 21:28:22.069695217 -0500 EST

2013-02-26 21:28:24.070985411 -0500 EST
2013-02-26 21:28:24.070999309 -0500 EST
2013-02-26 21:28:24.071002661 -0500 EST

发送语句

在通信开始之前,通道和值表达式都会被评估。通信会阻塞,直到发送可以进行。

returnCurrentTime() 表达式(时间戳)会立即被评估,而不是发送的时间戳。如果缓冲区已满,发送可能会稍后发生。

此外,测量实际的发送和接收时间,chan c 的缓冲延迟将是微不足道的:发送、发送、阻塞、接收、解除阻塞、发送。例如,

c <-  2013-02-26 23:29:34.505456624 -0500 EST
c <-  2013-02-26 23:29:34.505467030 -0500 EST
<- c  2013-02-26 23:29:34.505468497 -0500 EST
c <-  2013-02-26 23:29:34.505518015 -0500 EST

c <-  2013-02-26 23:31:36.506659943 -0500 EST
c <-  2013-02-26 23:31:36.506664832 -0500 EST
<- c  2013-02-26 23:31:36.506669302 -0500 EST
c <-  2013-02-26 23:31:36.506696540 -0500 EST
英文:

Effectively, you are running:

package main

import (
	&quot;fmt&quot;
	&quot;time&quot;
)

func main() {
	returnCurrentTime := func() string {
		return time.Now().String()
	}

	c := make(chan string, 2)

	asyncReturnCurrentTime := func(c chan string) {
		time.Sleep(2001 * time.Millisecond)
		c &lt;- returnCurrentTime()
	}

	go asyncReturnCurrentTime(c)
	go asyncReturnCurrentTime(c)
	go asyncReturnCurrentTime(c)
	fmt.Println(&lt;-c)
	fmt.Println(&lt;-c)
	fmt.Println(&lt;-c)
	fmt.Println()
	go asyncReturnCurrentTime(c)
	go asyncReturnCurrentTime(c)
	go asyncReturnCurrentTime(c)
	fmt.Println(&lt;-c)
	fmt.Println(&lt;-c)
	fmt.Println(&lt;-c)
	fmt.Println()
}

Output:

2013-02-26 21:28:22.069581655 -0500 EST
2013-02-26 21:28:22.069688722 -0500 EST
2013-02-26 21:28:22.069695217 -0500 EST

2013-02-26 21:28:24.070985411 -0500 EST
2013-02-26 21:28:24.070999309 -0500 EST
2013-02-26 21:28:24.071002661 -0500 EST

> Send statements
>
> Both the channel and the value expression are evaluated before
> communication begins. Communication blocks until the send can proceed.

The returnCurrentTime() expression (the timestamp) is evaluated immediately, before an attempt is made to send. It's not a timestamp for the send. The send may happen later if the buffer is full.

Also, measuring actual send and receive times, the buffering delay for chan c is going to be inconsequential: send, send, block, receive, unblock, send. For example,

c &lt;-;  2013-02-26 23:29:34.505456624 -0500 EST
c &lt;-;  2013-02-26 23:29:34.505467030 -0500 EST
&lt;- c;  2013-02-26 23:29:34.505468497 -0500 EST
c &lt;-;  2013-02-26 23:29:34.505518015 -0500 EST

c &lt;-;  2013-02-26 23:31:36.506659943 -0500 EST
c &lt;-;  2013-02-26 23:31:36.506664832 -0500 EST
&lt;- c;  2013-02-26 23:31:36.506669302 -0500 EST
c &lt;-;  2013-02-26 23:31:36.506696540 -0500 EST

huangapple
  • 本文由 发表于 2013年2月27日 09:29:18
  • 转载请务必保留本文链接:https://go.coder-hub.com/15102643.html
匿名

发表评论

匿名网友

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

确定