## 一、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、推薦文章