golang的channel剖析

wslzwps00321 · · 630 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

概述

  为了并发的goroutines之间的通讯,golang使用了管道channel。你可以通过一个goroutines向channel发送数据,然后从另一个goroutine接收它

通常我们会使用make来创建channel   -----   make(chan valType , [size])。

写入 channel    c<-

读取channel    <-c

分类


一   无缓冲

make创建时忽略第二个参数即可创建无缓冲channel实例如下:

// csdnTest.go
package main

import (
	"fmt"
        "time"
)

func main() {
	messages := make(chan string)
	go func() {
		messages <- "hello"
	}()
	msg := <-messages
      fmt.Println(msg)
}

值得注意的是:无缓冲channel只有接收和发送方都准备好了,channel才能正常工作,上述代码中如果我们把msg := <-messages这句注释掉,那么messages <- "hello"将会一直阻塞。

在这里做个猜想: 无缓冲的buffer只是直使用了ipc的pipe,pipe两端都打开时才能使用。“<-”被解释为打开写端,“->被解释为打开读端”


二  有缓冲

示例:

// csdnTest.go
package main

import (
	"fmt"
)

func main() {
	messages := make(chan string, 2)
	messages <- "hello"
	messages <- "world"

	fmt.Println(<-msg)
	fmt.Println(<-msg)
}

说明:当创建有缓冲的buffer时,就不需要有准备好的接收器来接受数据,数据可以存在channel的缓冲区中,有缓冲的channel有如下特性:

1:数据未满可以写,当数据满了再写阻塞。

2:数据未空可以读,当数据空了再读阻塞。

通过上面的特性可以做个大胆的猜测,有缓冲的是通过msg-queue来实现的。

有缓冲和无缓冲从底层的实现就不同,因为他们使用了不同的ipc。


三  非阻塞

正常情况下的channel读和写都是阻塞的,然而我们可以使用一个带有default的select来触发非阻塞的channle。原本select也是阻塞的,在加入default时select本身也变为非阻塞

举个例子:

// csdnTest.go
package main

import "fmt"

func main() {

	messages := make(chan string)
	signals := make(chan bool)

	//message和signals是无缓冲channel,因为接收器和发送器没同时准备好,所以channel无法使用。
	//下面所有select会输出default的内容

	select {
	case msg := <-messages:
		fmt.Println("received message", msg)
	default:
		fmt.Println("no message received")
	}

	msg := "hi"
	select {
	case messages <- msg:
		fmt.Println("sent message", msg)
	default:
		fmt.Println("no message sent")
	}

	select {
	case msg := <-messages:
		fmt.Println("received message", msg)
	case sig := <-signals:
		fmt.Println("received signal", sig)
	default:
		fmt.Println("no activity")
	}
}

有兴趣同学将上面无缓冲的改成有缓冲的尝试一下。

至于select为何在加入default后channel和自身都未非阻塞了,你可以去看看go源码select.go这个源文件,-------------------也可以阅读我的下一期博客。


四   channel的用例


4.1 处理超时

    c := make(chan string, 1)
    go func() {
        time.Sleep(time.Second * 3)
        c <- "inner goroutine"
    }()

    select {
    case res := <-c:
        fmt.Println(res)
    case <-time.After(time.Second * 1):
        fmt.Println("timeout")
    }


4.2 channel关闭

      关闭一个channel时,就不能向该channel中写数据了。通常我们用一个值返回值来接收channel的数据,但此时可以用两个返回值  (valeu,b:=<-c)   来捕获channel是否关闭   b取值false或true。

当我们处理一个需要结束的任务,可采用下面的方式来调度

	jobs := make(chan int, 10)
	done := make(chan bool)
	go func() {
		for {
			j, more := <-jobs
			if more {
				fmt.Println("received job", j)
			} else {
				fmt.Println("received all jobs")
				done <- true
				return
			}
		}
	}()

	for j := 1; j <= 3; j++ {
		jobs <- j
		fmt.Println("sent job", j)
	}
	close(jobs)

   

4.3  数据乒乓球式的流转


举一个golang官网比较知名的一个例子:

package main

import "fmt"

func ping(pings chan<- string, msg string) {
    pings <- msg
}

func pong(pings <-chan string, pongs chan<- string) {
    msg := <-pings
    pongs <- msg
}

func main() {
    pings := make(chan string, 1)
    pongs := make(chan string, 1)
    ping(pings, "passed message")
    pong(pings, pongs)
    fmt.Println(<-pongs)
}











谢谢各位的阅读


本文来自:CSDN博客

感谢作者:wslzwps00321

查看原文:golang的channel剖析

入群交流(和以上内容无关):Go中文网 QQ 交流群:798786647 或加微信入微信群:274768166 备注:入群;关注公众号:Go语言中文网

630 次点击  
加入收藏 微博
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传