<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                [TOC] ### **什么是channel** ***** #### **概述** go channel 存在3種狀態 * nil 未初始化的狀態,只進行了聲明 * active 正常的channel,可讀或者可寫 * closed 已關閉,**關閉后的channel != nil** ![Gmvb6O.png](https://s1.ax1x.com/2020/03/30/Gmvb6O.png) >有種特殊情況,當nil的通道在select的某個case中時,這個case會阻塞,但不會造成死鎖。 channel可進行`3種操作`: 1. 讀 2. 寫 3. 關閉 #### **基本讀寫操作** 定義一個channel: ``` ch := make(chan int) ``` 將一個數據寫入(發送)至channel: ``` ch <- value ``` 向channel寫入數據通常會導致程序阻塞,直到有其他goroutine從這個channel中讀取數據。從 channel中讀取數據的語法是: ``` value := <-ch ``` >如果channel之前沒有寫入數據,那么從channel中讀取數據也會導致程序阻塞,直到channel中被寫入數據為止。我們之后還會提到如何控制channel只接受寫或者只允許讀取,即單向channel。 ` ` ### **channel的使用場景** ***** channel用在**數據流動的地方**: 1. 消息傳遞、消息過濾 2. 信號廣播 3. 事件訂閱與廣播 4. 請求、響應轉發 5. 任務分發 6. 結果匯總 7. 并發控制 8. 同步與異步 9. ... ### **channel的基本操作和注意事項** ***** #### **使用for range讀channel** * 場景:當需要不斷從channel讀取數據時 * 原理:使用`for-range`讀取channel,這樣既安全又便利,當channel關閉時,for循環會自動退出,無需主動監測channel是否關閉,可以防止讀取已經關閉的channel,造成讀到數據為通道所存儲的數據類型的零值。 * 用法: ```go for x := range ch{ fmt.Println(x) } ``` #### **select原理** ``` select { case <-chan1: // 如果chan1成功讀到數據,則進行該case處理語句 case chan2 <- 1: // 如果成功向chan2寫入數據,則進行該case處理語句 default: // 如果上面都沒有成功,則進入default處理流程 } ``` #### **使用select處理多個channel** * 場景:需要對多個通道進行同時處理,但**只處理最先發生的channel時** * 原理:`select`可以同時監控多個通道的情況,只處理未阻塞的case。**當通道為nil時,對應的case永遠為阻塞,無論讀寫。特殊關注:普通情況下,對nil的通道寫操作是要panic的**。 * 用法: ``` var wg sync.WaitGroup func main() { c := make(chan int, 2) d := make(chan string, 2) wg.Add(1) go func() { defer wg.Done() c <- 1 d <- "joker" }() select { case x := <- c: fmt.Print(x) case y := <- d: fmt.Print(y) } wg.Wait() } //output 1 ``` ` ` #### **只讀/只寫channel** 只讀channel只能讀取,只寫channel只能寫入,更易讀,更安全 ``` 只寫通道:chan<- T 只讀通道:<-chan T ``` * 場景:協程對某個通道只讀或只寫時 * 目的:A. 使代碼更易讀、更易維護,B. 防止只讀協程對通道進行寫數據,但通道已關閉,造成panic。 * 用法: * 如果協程對某個channel只有寫操作,則這個channel聲明為只寫。 * 如果協程對某個channel只有讀操作,則這個channe聲明為只讀 ``` // 只有generator進行對outCh進行寫操作,返回聲明 // <-chan int,可以防止其他協程亂用此通道,造成隱藏bug func generator(int n) <-chan int { outCh := make(chan int) go func(){ for i:=0;i<n;i++{ outCh<-i } }() return outCh } // consumer只讀inCh的數據,聲明為<-chan int // 可以防止它向inCh寫數據 func consumer(inCh <-chan int) { for x := range inCh { fmt.Println(x) } } ``` ` ` #### **使用緩沖channel增強并發** 創建一個帶緩沖的channel: ``` c := make(chan int, 1024) ``` 在調用make()時將緩沖區大小作為第二個參數傳入即可,比如上面這個例子就創建了一個大小為1024的int類型channel,即使沒有讀取方,寫入方也可以一直往channel里寫入,在緩沖區被填完之前都不會阻塞。 ` ` 從帶緩沖的channel中讀取數據可以使用與常規非緩沖channel完全一致的方法,但我們也可以使用range關鍵來實現更為簡便的循環讀取: ``` for i := range c { fmt.Println("Received:", i) } ``` ` ` #### **超時機制** 在并發編程的通信過程中,**最需要處理的就是超時問題**,即**向channel寫數據時發現channel已滿**,或者**從channel試圖讀取數據時發現channel為空**。如果不正確處理這些情況,很**可能會導致整個goroutine鎖死**。 ` ` Go語言沒有提供直接的超時處理機制,但我們可以利用select機制。雖然select機制不是專為超時而設計的,卻能很方便地解決超時問題。因為select的特點是只要其中一個case已經完成,程序就會繼續往下執行,而不會考慮其他case的情況: ``` // 首先,我們實現并執行一個匿名的超時等待函數 timeout := make(chan bool, 1) go func() { time.Sleep(1e9) // 等待1秒鐘 timeout <- true }() // 然后我們把timeout這個channel利用起來 select { case <-ch: // 從ch中讀取到數據 case <-timeout: // 一直沒有從ch中讀取到數據,但從timeout中讀取到了數據 } ``` 這樣使用select機制可以避免永久等待的問題,因為程序會在timeout中獲取到一個數據后繼續執行,無論對ch的讀取是否還處于等待狀態,從而達成1秒超時的效果。 ` ` >這種寫法看起來是一個小技巧,但卻是在Go語言開發中避免channel通信超時的最有效方法。在實際的開發過程中,這種寫法也需要被合理利用起來,從而有效地提高代碼質量。 ` ` #### **channel傳遞** 在Go語言中channel本身也是一個**原生類型**,**與map之類的類型地位一樣**,因此channel本身在定義后也可以通過channel來傳遞。 我們可以使用這個特性來實現Linux上非常常見的管道(pipe)特性。管道也是使用非常廣泛的一種設計模式,比如在處理數據時,我們可以采用管道設計,這樣可以比較容易以插件的方式增加數據的處理流程。 ` ` #### **單向channel** 假如一個channel真的只能讀,那么肯定只會是空的,因為你沒機會往里面寫數據。同理,如果一個channel只允許寫,即使寫進去了,也沒有絲毫意義,因為沒有機會讀取里面的數據。所謂的單向channel概念,其實只是對channel的一種使用限制。 ` ` 在將一個channel變量傳遞到一個函數時,可以通過將其指定為單向channel變量,從而限制 該函數中可 以對此 channel的操作, 比如只能往 這個 channel寫,或者只 能從這個channel讀. 單向channel變量的聲明非常簡單,如下: ``` var ch1 chan int // ch1是一個正常的channel,不是單向的 var ch2 chan<- float64// ch2是單向channel,只用于寫float64數據 var ch3 <-chan int // ch3是單向channel,只用于讀取int數據 ``` 基于ch4,我們通過類型轉換初始化了兩個單向channel:單向讀的ch5和單向寫的ch6。 ` ` 為什么要做這樣的限制呢?從設計的角度考慮,所有的代碼應該都遵循“最小權限原則”,從而避免沒必要地使用泛濫問題,進而導致程序失控。 單向channel的用法: ``` func Parse(ch <-chan int) { for value := range ch { fmt.Println("Parsing value", value) } } ``` 除非這個函數的實現者無恥地使用了類型轉換,否則這個函數就不會因為各種原因而對ch進行寫,避免在ch中出現非期望的數據,從而很好地實踐最小權限原則。 ` ` #### **關閉channel** 關閉channel的語句非常簡單 ``` close(ch) ``` #### **使用`_,ok`判斷channel是否關閉** * 場景:讀channel,但不確定channel是否關閉時 * 原理:讀已關閉的channel會得到零值,如果不確定channel,需要使用`ok`進行檢測。ok的結果和含義: * `true`:讀到數據,并且通道沒有關閉。 * `false`:通道關閉,無數據讀到。 * 用法: ``` if v, ok := <- ch; ok { fmt.Println(v) } ```
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看