channel

channel

单纯的将函数执行并发是没有意义的,函数与函数间需要交换数据才能体现并发执行函数的意义。虽然可以使用内存进行数据交换,但是共享内存在不同的goroutine中容易发生竞态问题。

为了保证数据交换的正确性,必须使用互斥锁对内存进行加锁,这种做法势必造成性能问题。

Go语言的并发模型是CSP,提倡通过通信共享内存而不是通过共享内存而实现通信。

如果说goroutine是执行Go程序并发的执行体,channel就是它们之间的链接。channel是一个可以让goroutine发送特定值到另一个goroutine的通信机制。

Go中的channel是一种特殊的类型。通道像一个传送带或者队列,总是遵循先入先出的规则,保证收发数据的顺序。每一个通道都是一个具体类型的导管,就是说声明channel的时候需要为其指定元素类型。

channel类型

 声明channel类型的格式如下

var 变量 chan 元素类型

示例:

var ch1 chan int      // 声明一个传递整型的通道
var ch2 chan string   // 声明一个传递字符串类型的通道

 

创建channel

通道是引用类型,通道的空值是nil

var  ch  chan int
fmt.Println(ch)   // <nil>

声明的通道需要使用make函数初始化后才能使用

make(chan 元素类型,[缓冲大小])


缓冲大小是可选的,如果没有缓冲区大小,则必须有接收者接收,否则会卡住。

  

var ch1 chan string           // 声明
ch1 = make(chan string, 10)   // 初始化,必须使用make初始化,否则不能用
for i := 1; i < 10; i++ {
	str := "lifq" + strconv.Itoa(i)
	ch1 <- str
}

v1 := <-ch1
v2 := <-ch1
v3 := <-ch1
fmt.Println(v1, v2, v3)  // lifq1 lifq2 lifq3

 

channel 操作

发送

将一个值发送到通道中

ch  <-  10

  

接收

从一个通道中接收一个值

x  :=  <- ch
<- ch 从ch中接收值,忽略结果

关闭

通过调用内置的close函数来关闭通道

close(ch)

 关闭通道需要注意的是,只有在通知接收方goroutine所有的数据全部发送完毕的时候才需要关闭通道。通道是可以被垃圾回收机制回收的,它和关闭文件不一样,在操作结束后关闭文件是必须的,但是关闭通道不是必需的。

 

channel案例

需求:

1.  启动一个goroutine,生成100个数发送到ch1
2.  启动一个goroutin,从ch1中取值,计算它的平方放到ch2
package main
import (
	"fmt"
	"sync"
)
var wg sync.WaitGroup
func f1(ch1 chan int) {
	defer wg.Done()
	for i := 0; i < 60; i++ {
		ch1 <- i
	}
	close(ch1)
}

func f2(ch1 chan int, ch2 chan int) {
	defer wg.Done()
	for {
		x, ok := <-ch1
		if !ok {
			break
		}
		ch2 <- x * x
	}
	close(ch2)
}

func main() {
	a := make(chan int, 100)
	b := make(chan int, 100)
	wg.Add(2)
	go f1(a)
	go f2(a, b)
	wg.Wait()
	for {
		i, ok := <-b
		if !ok {
			break
		}
		fmt.Println(i)
	}
}
posted @ 2022-12-12 17:15  羊脂玉净瓶  阅读(392)  评论(0)    收藏  举报