无缓冲通道

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

Unbuffered channels

问题

我正在进行Go Tour:http://tour.golang.org/#72
这是我的代码:

package main

import (
    "code.google.com/p/go-tour/tree"
    "fmt"
)

// Walk遍历树t,将树中的所有值发送到通道ch。
func Walk(t *tree.Tree, ch chan int) {
    var makeWalk func(t *tree.Tree, ch chan int)
    
    makeWalk = func(t *tree.Tree, ch chan int) {
        if t.Left != nil {
            makeWalk(t.Left, ch)
        }
        fmt.Println("-->", t.Value)
        ch <- t.Value
        fmt.Println("继续这里")
        if t.Right != nil {
            makeWalk(t.Right, ch)
        }
    }
    makeWalk(t, ch)
    close(ch)
}

// Same确定树t1和t2是否包含相同的值。
func Same(t1, t2 *tree.Tree) bool {
    //   var ch_l chan int = make(chan int)
    //	var ch_r chan int = make(chan int)
    return false
}

func main() {
    ch := make(chan int)
    go Walk(tree.New(1), ch)    
    for i := range(ch) {
        fmt.Println(i)
    }
}

这是输出:

--> 1
继续这里
--> 2
1
2
继续这里
--> 3
继续这里
--> 4
3
4
继续这里
--> 5
继续这里
--> 6
5
6
继续这里
--> 7
继续这里
--> 8
7
8
继续这里
--> 9
继续这里
--> 10
9
10
继续这里

据我理解,当通道传递值时,通道会阻塞。我期望看到以下输出:

--> 1
1
继续这里
--> 2
2
继续这里
...
--> 10
10
继续这里

通道是非缓冲的,fmt.Println 是有缓冲的吗?这里发生了什么? 无缓冲通道

英文:

I'm doing the Go Tour: http://tour.golang.org/#72
This is my code:

package main

import (
    &quot;code.google.com/p/go-tour/tree&quot;
    &quot;fmt&quot;
)

// Walk walks the tree t sending all values
// from the tree to the channel ch.
func Walk(t *tree.Tree, ch chan int) {
    var makeWalk func(t *tree.Tree, ch chan int)
    
    makeWalk = func(t *tree.Tree, ch chan int) {
        if t.Left != nil {
            makeWalk(t.Left, ch)
        
        }
        fmt.Println(&quot;--&gt;&quot;, t.Value)
        ch &lt;- t.Value
        fmt.Println(&quot;continue here&quot;)
        if t.Right != nil {
            makeWalk(t.Right, ch)
        }
    }
    makeWalk(t, ch)
    close(ch)
}

// Same determines whether the trees
// t1 and t2 contain the same values.
func Same(t1, t2 *tree.Tree) bool {
 //   var ch_l chan int = make(chan int)
 //	var ch_r chan int = make(chan int)
	return false
}

func main() {
    ch := make(chan int)
    go Walk(tree.New(1), ch)    
    for i := range(ch) {
    	fmt.Println(i)
    }
}

This is the output:

--&gt; 1
continue here
--&gt; 2
1
2
continue here
--&gt; 3
continue here
--&gt; 4
3
4
continue here
--&gt; 5
continue here
--&gt; 6
5
6
continue here
--&gt; 7
continue here
--&gt; 8
7
8
continue here
--&gt; 9
continue here
--&gt; 10
9
10
continue here

As far as I understand channels block when they are passed with values. I expect to see an output like this:

--&gt; 1
1
continue here
--&gt; 2
2
continue here
...
--&gt; 10
10
continue here

Channels are not buffered, is fmt.Println buffered? What happens here? 无缓冲通道

答案1

得分: 1

当你提到fmt.Println时,你是正确的。通道的读写不是调度器切换到另一个goroutine的唯一时机。阻塞的系统调用也可以触发上下文切换。

根据FAQ

当一个协程阻塞,比如调用一个阻塞的系统调用时,运行时会自动将同一操作系统线程上的其他协程移动到一个不同的可运行线程,以防止它们被阻塞。

fmt.Println最终会调用一个阻塞的系统调用(write()),所以你看到这种行为。

英文:

You're on the right track when you mention fmt.Println. Channel reads and writes are not the only time the scheduler can switch to another goroutine. A blocking system call can also trigger a context switch.

From the FAQ:

> When a coroutine blocks, such as by calling a blocking system call,
> the run-time automatically moves other coroutines on the same
> operating system thread to a different, runnable thread so they won't
> be blocked.

fmt.Println will ultimately call a blocking system call (write()), so that's why you're seeing this behavior.

huangapple
  • 本文由 发表于 2014年6月28日 22:58:51
  • 转载请务必保留本文链接:https://go.coder-hub.com/24468052.html
匿名

发表评论

匿名网友

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

确定