单纯将函数实现并发是没有任何意义的,函数与函数之间需要交换数据才能够体现并发执行函数的意义。
虽然可以使用共享内存进行数据交换,但是共享内存在不同的goroutine
中容易发生竞态情况。为了保证数据交换的正确性,必须使用互斥量对内存进行加锁,这种做法肯定会造成性能影响。
Go语言的并发模型是CSP
,提倡通过通信共享内存而不是通过共享内存而实现通信。
如果说goroutine
是Go程序并发的执行体,channel
就是他们之间的连接。channel
是可以让一个goroutine
发送特定值到另一个goroutine
的通信机制。
Go语言中的通道(channel)是一种特殊的类型,通道像一个传送带或者队列,总是遵循先入先出(FIFO)的规则,保证收发数据的顺序。每一个通道都是一个具体类型的导管,也就是声明channel
的时候需要为其制定元素类型。
声明通道类型的格式如下:
var 变量 chan 元素类型
举几个例子
var a1 chan int // 声明一个int类型的chan
var a2 chan str // 声明一个str类型的chan
var a3 chan []int // 声明一个int slice的chan
channel是一个引用类型,往channel里写入数据相当于是拿到其内存地址操作,必须使用make函数初始化(通道类型的控制是nil
)
package main
import "fmt"
var a chan int
func main(){
a = make(chan int,15) // 通道初始化(带缓冲区)
fmt.Println(a) // 0xc000098000
}
通道有发送(send)、接收(receive)和关闭(close)三种操作,发送和接收都是用<-
符号。
先定义一个通道
b := make(chan int,15)
发送
将一个值发送到通道中
b <- 20 // 把20发送到b中
接收
x := <- b // 从b中接收值并赋值给变量x
<-b // 从b中接收值
关闭
通过调用内置的close
函数来关闭通道
close(b)
关于关闭通道需要注意的是,只有在通知接收方goroutine所有的数据都是发送完毕的时候才需要关闭通道。通道是可以被垃圾回收机制回收的,它和关闭文件机制是不一样的,在结束操作之后关闭文件时必须要做的,但是关闭通道不是必须的。
需求:
1.启动一个goroutine,生成100个数发送到ch1
2.启动一个goroutine,从ch1中取值,计算其平方放到ch2中
3.在main中,从ch2取值打印出来
代码:
package main
import (
"fmt"
"sync"
)
var wg sync.WaitGroup
func f1(ch1 chan int){
defer wg.Done()
for i:=0;i<100;i++{
ch1 <- i
}
close(ch1)
}
func f2(ch1,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 ret := range b {
fmt.Println(ret)
}
}
原文:https://www.cnblogs.com/jasonminghao/p/12348281.html