# 通道
* * * * *
--: 作者:Mick
時間:2018年11月21日
* * * * *
* [ ] 什么是通道
* [ ] 通道是解決什么問題的
* [ ] 通道的分類
* [ ] 通道的使用
### 思考問題
1:并發讀取文件夾下面的文件,假設文件夾下有9文件,每個文件都會進行大量處理(使用沉睡1秒的方式模擬處理),怎9個文件處理完畢需要9秒,問如何使用并行+goroutine縮短這時間
2:受線程數量的影響goroutinue的數量超過線程則是并發,要求是goroutinue的數量和線程數一直量處理文件
```
package main
import (
"path/filepath"
"os"
"time"
"io/ioutil"
"fmt"
"sync"
)
var wg sync.WaitGroup // 問題1
var filePath string = "./static/file"
var files []string
func init(){
err := filepath.Walk(filePath,func(path string,info os.FileInfo,err error)error{
if info.IsDir() {
return nil
}
files = append(files,path)
return nil
})
if err != nil{
panic(err)
}
}
func main(){
questionTwo()
}
// 如何控制goroutinue的數量,來并發并行的計算
func questionTwo(){
fileChan := make(chan string)
fileNum := len(files)
wg.Add(fileNum)
for i:=0;i<fileNum;i++{
go ReadFileChan(fileChan)
}
for _,v := range files {
fileChan <- v
}
wg.Wait()
close(fileChan)
}
func ReadFileChan(fileChan chan string){
defer wg.Done()
filePath := <- fileChan
time.Sleep(time.Second)
data,err := ioutil.ReadFile(filePath)
fmt.Println(string(data),err)
}
//采用一個文件一個goroutinue的方式,如果文件是幾十萬個,則開啟幾十萬個goroutinue
func questionOne(){
//wg.Add(len(files)) 問題1
for _,v := range files {
//go ReadFile(v) 問題1
ReadFile(v)
}
//wg.Wait() 問題1
}
func ReadFile(filePath string){
//defer wg.Done() 問題1
time.Sleep(time.Second)
data,err := ioutil.ReadFile(filePath)
fmt.Println(string(data),err)
}
```
### 無緩沖通道
示例1:打網球示例
```
func PlayerExam(){
court := make(chan int)
wg.Add(2)
go Player("Jack",court)
go Player("Tom",court)
court <- 1
wg.Wait()
}
func Player(name string,court chan int){
defer wg.Done()
for{
ball,ok := <- court
if !ok {
fmt.Printf("Player %s Win\n",name)
return
}
n := rand.Intn(100)
if n %13 == 0{
fmt.Printf("Player %s Missed %d\n",name,ball)
close(court)
return
}
fmt.Printf("Player %s Hit %d\n",name,ball)
ball ++
court <- ball
}
}
```
示例2:接力賽示例
```
func init(){
rand.Seed(time.Now().UnixNano())
}
var wg sync.WaitGroup
func main(){
baton := make(chan int)
wg.Add(1)
go Runner(baton)
baton <- 1
wg.Wait()
}
func Runner(b chan int){
var newRunner int
runner := <- b
fmt.Printf("Runner %d Running With Baton\n",runner)
time.Sleep(time.Millisecond*500)
if runner == 4{
fmt.Printf("Runner %d Finish Race\n",runner)
wg.Done()
}else{
newRunner = runner + 1
fmt.Printf("Runner %d To The Line\n",newRunner)
go Runner(b)
fmt.Printf("Runner %d Exchange With Runner %d\n",runner,newRunner)
b <- newRunner
}
}
```
### 有緩沖通道
示例1:并發工作
```
package main
import (
"fmt"
"sync"
"time"
)
type Work struct{
tasks chan string
wg sync.WaitGroup
}
func(w *Work) Producter(n int){
w.tasks = make(chan string,n)
}
func(w *Work) Worker(id int,task chan string){
defer w.wg.Done()
for v := range task{
fmt.Printf("Worker %d Begin %s\n",id,v)
time.Sleep(time.Millisecond * 200)
fmt.Printf("Worker %d Finish %s\n",id,v)
}
}
func(w *Work) ShutDown(){
w.wg.Wait()
fmt.Println("Work Over")
}
func main(){
goNum := 10
w := Work{}
w.Producter(goNum)
w.wg.Add(goNum)
for i:=0;i<10;i++{
go w.Worker(i,w.tasks)
}
for i:=0;i<100;i++{
w.tasks <- fmt.Sprintf("Task %d",i)
}
close(w.tasks)
w.wg.Wait()
}
```
### 小結
**??并發是指 goroutine 運行的時候是相互獨立的。
??使用關鍵字 go 創建 goroutine 來運行函數。
??goroutine 在邏輯處理器上執行,而邏輯處理器具有獨立的系統線程和運行隊列。
??競爭狀態是指兩個或者多個 goroutine 試圖訪問同一個資源。
??原子函數和互斥鎖提供了一種防止出現競爭狀態的辦法。
??通道提供了一種在兩個 goroutine 之間共享數據的簡單方法。
??無緩沖的通道保證同時交換數據,而有緩沖的通道不做這種保證。**