## 一、channel是什么
* goroutine运行在相同的地址空间,因此访问共享内存必须做好同步,处理好线程安全问题
* goroutine奉行通过通信来共享内存,而不是共享内存来通信
* channel是一个引用类型,用于多个goroutine通讯,其内部实现了同步,确保并发安全
* channel主要用域多个协程之间通信
## 二、channel的基本使用
* channel可以用内置make()函数创建
* 定义一个channel时,也需要定义发送到channel的值的类型
make(chan 类型)
make(chan 类型, 容量)
* 当 capacity= 0 时,channel 是无缓冲阻塞读写的,当capacity> 0 时,channel 有缓冲、是非阻塞的,直到写满 capacity个元素才阻塞写入
* channel通过操作符<-来接收和发送数据,发送和接收数据语法:
channel <- value //发送value到channel
<-channel //取出数据扔掉
x := <-channel //取出数据,给x
x, ok := <-channel //功能同上,顺便检查一下通道
## 三、channel实现主协程和子协程通信
~~~
package main
import (
"fmt"
)
func main() {
// 创建协程
h := make(chan int)
// 创建子协程
go func(){
defer fmt.Println("子协程结束")
fmt.Println("子协程正在运行")
h <- 999
}()
num := <-h
fmt.Println("num=",num)
fmt.Println("main结束")
}
~~~
运行结果:
子协程正在运行
子协程结束
num= 999
main结束
## 四、无缓冲 channel
~~~
package main
import (
"fmt"
"time"
)
func main() {
//无缓冲通道
c := make(chan int, 0)
fmt.Printf("len(c)=%d,cap(c)=%d\n", len(c), cap(c))
//子协程存数据
go func() {
defer fmt.Println("子协程结束")
//向通道添加数据
for i := 0; i < 3; i++ {
c <- i
fmt.Printf("子协程正在运行[%d]:"+"len(c)=%d,cap(c)=%d\n", i, len(c), cap(c))
}
}()
time.Sleep(2 * time.Second)
//主协程取数据
for i := 0; i < 3; i++ {
num := <-c
fmt.Println("num = ", num)
}
fmt.Println("主协程结束")
}
~~~
运行结果:
len(c)=0,cap(c)=0
num = 0
子协程正在运行[0]:len(c)=0,cap(c)=0
子协程正在运行[1]:len(c)=0,cap(c)=0
num = 1
num = 2
主协程结束
## 五、有缓冲 channel
~~~
package main
import (
"fmt"
"time"
)
func main() {
//有缓冲通道
c := make(chan int, 3)
fmt.Printf("len(c)=%d,cap(c)=%d\n", len(c), cap(c))
//子协程存数据
go func() {
defer fmt.Println("子协程结束")
//向通道添加数据
for i := 0; i < 3; i++ {
c <- i
fmt.Printf("子协程正在运行[%d]:"+"len(c)=%d,cap(c)=%d\n", i, len(c), cap(c))
}
}()
time.Sleep(2 * time.Second)
//主协程取数据
for i := 0; i < 3; i++ {
num := <-c
fmt.Println("num = ", num)
}
fmt.Println("主协程结束")
}
~~~
执行结果:
len(c)=0,cap(c)=3
子协程正在运行[0]:len(c)=1,cap(c)=3
子协程正在运行[1]:len(c)=2,cap(c)=3
子协程正在运行[2]:len(c)=3,cap(c)=3
子协程结束
num = 0
num = 1
num = 2
主协程结束
## 六、close()
* 可以通过内置的close()函数关闭channel
* 通知取通道数据的地方,没有数据了,不用取数据了
#### 没有close()
~~~
package main
import (
"fmt"
)
func main() {
c := make(chan int)
//子协程存数据
go func() {
for i := 0; i < 5; i++ {
c <- i
}
}()
//主协程取数据
for {
if data, ok := <-c; ok {
fmt.Println(data)
} else {
break
}
}
fmt.Println("主协程结束")
}
~~~
这里的运行结果会出现报错,因为我们的主协程是死循环,会一致执行
#### 有close()
~~~
package main
import (
"fmt"
)
func main() {
c := make(chan int)
//子协程存数据
go func() {
for i := 0; i < 5; i++ {
c <- i
}
close(c)
}()
//主协程取数据
for {
if data, ok := <-c; ok {
fmt.Println(data)
} else {
break
}
}
fmt.Println("主协程结束")
}
~~~
运行结果:
0
1
2
3
4
主协程结束
注:这里子协程会有一个通知主协程的动作,如果没有更多数据,子协程会通知主协程,没有更多数据可以结束取数据的动作了
## 七、单方向的channel
### 1、概念
* 默认情况下,通道是双向的,也就是,既可以往里面发送数据也可以接收数据
* go可以定义单方向的通道,也就是只发送数据或者只接收数据,声明如下
var ch1 chan int //正常
var ch2 chan<- float64 //只能写float64
var ch3 <-chan int //只能读int类型数据
* 可以将 channel 隐式转换为单向队列,只收或只发,不能将单向 channel 转换为普通channel
#### 普通实例1
~~~
package main
import (
"fmt"
)
func main() {
// 定义一个正常的通道
c := make(chan int,3)
// 转换为只写通道
var send chan<- int =c
// 转化为只读
var recv <-chan int =c
// 写数据
send <- 11
// 读数据
<- recv
fmt.Println("结束")
}
~~~
#### 单方向的channel 可以用作生产者和消费者
~~~
package main
import "fmt"
//生产者,只写
func producter(out chan<- int) {
defer close(out)
for i := 0; i < 5; i++ {
out <- i
}
}
//消费者,只读
func consumer(in <-chan int) {
for num := range in {
fmt.Println(num)
}
}
func main() {
//正常通道
c := make(chan int)
//生产者
//可以将 channel 隐式转换为单向队列,只收或只发,不能将单向 channel 转换为普通channel
go producter(c)
//消费者
consumer(c)
fmt.Println("done")
}
~~~
执行结果:
0
1
2
3
4
done
注:可以将 channel 隐式转换为单向队列,只收或只发,不能将单向 channel 转换为普通channel
- 一、数组
- 二、切片
- 三、copy
- 四、MAP
- 五、结构体
- 六、结构体参数
- 七、面向”对象“
- 1、匿名字段
- 2、方法
- 3、包和封装
- 4、接口
- 5、异常处理
- 八、Json
- 九、文件操作
- 1、写文件
- 2、读取文件内容
- 3、拷贝文件
- 十、反射
- 1、查看类型,字段和方法
- 2、查看匿名字段
- 3、修改基本类型的值
- 4、修改结构体的值
- 5、调用方法
- 十一、并发编程
- 1、并行和并发
- 2、协程
- 3、runtime包
- 5、channel的使用
- 6、close
- 7、定时器
- 8、select
- 9、协程同步锁
- 十二、socket编程
- 十三、Http编程
- 十四、并发爬虫和数据处理
- 1、简易爬虫实例
- 2、并发爬取图片
- 3、读文件中的数据
- 4、数据清洗
- 其他
- 1、推荐文章