Go chan基础1

值表示
通道类型是一个引用类型,所以初始化前,为此类型的零值:nil。
初始化通道
缓冲通道(10):
make(chan int,10)
make(chan int)
非缓冲通道:
make(chan int)
一个通道值的缓冲容量总是固定不变的。如果第二个参数值被省略了,就表示被初始化的这个通道永远无法缓冲任何元素值。发送给它的元素值应该被立刻取走,否则发送方的goroutine就会被暂停(或者说阻塞),直到有接收方接收这个元素值。
需要特别注意:
试图从一个未被初始化的通道值(即值为nil的通道)那里接收值,会造成当前goroutine的永久阻塞!
同样,向nil 通道 发送值,goroutine也会被永久阻塞!
操作特性
通道是在多个goroutine之间传递数据和同步数据的重要手段,而对通道的操作本身也是同步的。
在同一时刻,仅有一个goroutine能向一个通道发送值,
同时也仅有一个goroutine能接收值。
在通道中,先进先出,相当于FIFO的消息队列。
通道中的每个值都只可能被某一个goroutine接收,已接受的值会立刻从通道中删除。

Go语言中 空结构类型的变量是不占用任何内存空间的,
并且所有该类型的变量都拥有相同的内存地址。
建议用于传递"信号"的通道,都以struct{}作为元素类型,除非需要传递更多信息。

package main

import (
	"fmt"
	"time"
)

var strChan = make(chan string, 3)

func main() {

	// 通知接受操作chan
	syncChan1 := make(chan struct{}, 1)
	// 避免主goroutine过早结束
	syncChan2 := make(chan struct{}, 2)
	go func() { // 用于接收操作。
		<-syncChan1
		fmt.Println("Received a sync signal and wait a second... [receiver]")
		time.Sleep(time.Second)
		for {
			if elem, ok := <-strChan; ok {
				fmt.Println("Received:", elem, "[receiver]")
			} else {
				break
			}
		}
		fmt.Println("Stopped. [receiver]")
		syncChan2 <- struct{}{}
	}()
	go func() { // 用于发送操作。
		for _, elem := range []string{"a", "b", "c", "d"} {
			strChan <- elem
			fmt.Println("Sent:", elem, "[sender]")
			if elem == "c" {
				syncChan1 <- struct{}{}
				fmt.Println("Sent a sync signal. [sender]")
			}
		}
		fmt.Println("Wait 2 seconds... [sender]")
		time.Sleep(time.Second * 2)
		close(strChan)
		syncChan2 <- struct{}{}
	}()
	<-syncChan2
	<-syncChan2
}

//输出
Sent: a [sender]
Sent: b [sender]
Sent: c [sender]
Sent a sync signal. [sender]
Received a sync signal and wait a second... [receiver]
Received: a [receiver]
Received: b [receiver]
Received: c [receiver]
Received: d [receiver]
Sent: d [sender]
Wait 2 seconds... [sender]
Stopped. [receiver]

在这里插入图片描述

除此之外,如果有多个goroutine因向同一个已满的通道发送元素值而被阻塞,那么当该通道中有多余空间的时候,最早被阻塞的那个goroutine会最先被唤醒。对于接收操作来说,也是如此,一旦已空的通道中有了新的元素值,那么最早因从该通道接收元素值而被阻塞的那个goroutine会最先被唤醒。
并且, Go运行时系统每次只会唤醒一个goroutine。

还有一点需要注意:发送方向通道发送的值会被复制,接收方接收的总是该值的副本,而不是该值本身。经由通道传递的值至少会被复制一次,至多会被复制两次。
例如,当向一个已空的通道发送值,且已有至少一个接收方因此等待时,该通道会绕过本身的缓冲队列,直接把这个值复制给最早等待的那个接收方。
又例如,当从一个已满的通道接收值,且已有至少一个发送方因此等待时,该通道会把缓冲队列中最早进入的那个值复制给接收方,再把最早等待的发送方要发送的数据复制到那个值的原先位置上。通道的缓冲队列属于环形队列,所以这样做是没问题的。
这种情况下,涉及的那两个值在传递到接收方之前都会被复制两次。
因此,当接收方从通道接收到一个值类型的值时,对该值的修改就不会影响到发送方持有的那个源值。但对于引用类型的值来说,这种修改会同时影响收发双方持有的值。

// chanval1.go
package main

import (
	"fmt"
	"time"
)

var mapChan = make(chan map[string]int, 1)

func main() {
	syncChan := make(chan struct{}, 2)
	go func() { // 用于演示接收操作。
		for {
			if elem, ok := <-mapChan; ok {
				elem["count"]++
			} else {
				break
			}
		}
		fmt.Println("Stopped. [receiver]")
		syncChan <- struct{}{}
	}()
	go func() { // 用于演示发送操作。
		countMap := make(map[string]int)
		for i := 0; i < 5; i++ {
			mapChan <- countMap
			time.Sleep(time.Millisecond)
			fmt.Printf("The count map: %v. [sender]\n", countMap)
		}
		close(mapChan)
		syncChan <- struct{}{}
	}()
	<-syncChan
	<-syncChan
}

//输出
The count map: map[count:1]. [sender]
The count map: map[count:2]. [sender]
The count map: map[count:3]. [sender]
The count map: map[count:4]. [sender]
The count map: map[count:5]. [sender]
Stopped. [receiver]

在这里插入图片描述

chan val 值类型

// chanval2.go
package main

import (
	"fmt"
	"time"
)

// Counter 代表计数器的类型。
type Counter struct {
	count int
}

// 引用类型,其值类型属性不会被修改
var mapChan = make(chan map[string]Counter, 1)
//var mapChan = make(chan map[string]*Counter, 1)

func main() {
	syncChan := make(chan struct{}, 2)
	go func() { // 用于演示接收操作。
		for {
			if elem, ok := <-mapChan; ok {
				counter := elem["count"]
				counter.count++
			} else {
				break
			}
		}
		fmt.Println("Stopped. [receiver]")
		syncChan <- struct{}{}
	}()
	go func() { // 用于演示发送操作。
		countMap := map[string]Counter{
			"count": Counter{},
		}
		//countMap := map[string]*Counter{
		//	"count": &Counter{},
		//}
		for i := 0; i < 5; i++ {
			mapChan <- countMap
			time.Sleep(time.Millisecond)
			fmt.Printf("The count map: %v. [sender]\n", countMap)
		}
		close(mapChan)
		syncChan <- struct{}{}
	}()
	<-syncChan
	<-syncChan
}

//输出
The count map: map[count:{0}]. [sender]
The count map: map[count:{0}]. [sender]
The count map: map[count:{0}]. [sender]
The count map: map[count:{0}]. [sender]
The count map: map[count:{0}]. [sender]
Stopped. [receiver]

chan val 引用类型

package main

import (
	"fmt"
	"time"
)

// Counter 代表计数器的类型。
type Counter struct {
	count int
}

//var mapChan = make(chan map[string]Counter, 1)
// 指针引用类型的指针类型,其值类型属性 会被修改
var mapChan = make(chan map[string]*Counter, 1)

func main() {
	syncChan := make(chan struct{}, 2)
	go func() { // 用于演示接收操作。
		for {
			if elem, ok := <-mapChan; ok {
				counter := elem["count"]
				counter.count++
			} else {
				break
			}
		}
		fmt.Println("Stopped. [receiver]")
		syncChan <- struct{}{}
	}()
	go func() { // 用于演示发送操作。
		//countMap := map[string]Counter{
		//	"count": Counter{},
		//}
		countMap := map[string]*Counter{
			"count": &Counter{},
		}
		for i := 0; i < 5; i++ {
			mapChan <- countMap
			time.Sleep(time.Millisecond)
			fmt.Printf("The count map: %v. [sender]\n", countMap)
		}
		close(mapChan)
		syncChan <- struct{}{}
	}()
	<-syncChan
	<-syncChan
}

The count map: map[count:0xc00001a078]. [sender]
The count map: map[count:0xc00001a078]. [sender]
The count map: map[count:0xc00001a078]. [sender]
The count map: map[count:0xc00001a078]. [sender]
The count map: map[count:0xc00001a078]. [sender]
Stopped. [receiver]

关闭通道

package main

import "fmt"

func main() {
	dataChan := make(chan int, 5)
	syncChan1 := make(chan struct{}, 1)
	syncChan2 := make(chan struct{}, 2)
	go func() { // 用于演示接收操作。
		<-syncChan1
		for {
			if elem, ok := <-dataChan; ok {
				fmt.Printf("Received: %d [receiver]\n", elem)
			} else {
				break
			}
		}
		fmt.Println("Done. [receiver]")
		syncChan2 <- struct{}{}
	}()
	go func() { // 用于演示发送操作。
		for i := 0; i < 5; i++ {
			dataChan <- i
			fmt.Printf("Sent: %d [sender]\n", i)
		}
		close(dataChan)
		syncChan1 <- struct{}{}
		fmt.Println("Done. [sender]")
		syncChan2 <- struct{}{}
	}()
	<-syncChan2
	<-syncChan2
}

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值