go語言開發基礎40 - 之go語言里的多線程(goroutine)通過管道(channel)實現多線程通訊


線程(goroutine)與管道(channel)的基本使用前面兩篇文章已經介紹了,這篇文章介紹下多線程通訊與多線程操作管道。

 

實例

1.1、一個線程往管道里寫數據、另一個線程從管道里讀數據示例

package main

import (
    "fmt"
    "time"
)

func writeChan(pi chan string) {
    for i := 0; i < 10; i++ {
        teststr := fmt.Sprintf("input channel data %s", string(i))
        pi <- teststr
    }
    fmt.Println("write channel end len:", len(pi))
}
func readChan(pi chan string) {
    var str string
    for i := 0; i < 10; i++ {
        str = <-pi
        fmt.Println("read channel data:", str)
    }
}

func main() {
    var strChan chan string        // 定義管道
    strChan = make(chan string, 10)       // 初始化管道的長度
    go writeChan(strChan)      // 啟動往管道里寫數據的線程
    go readChan(strChan)       // 啟動從管道里讀數據的線程
    time.Sleep(time.Second)     // sleep 1秒,等兩個線程結束
}

 

1.2、多線程判斷一千以內的素數的例子

素數又叫質數,質數是指在大於1的自然數中,除了1和它本身以外,不能被其他自然數整除的數。最小的質數是2,它也是唯一的偶數質數,最前面的質數依次排列為:2、3、5、7、11、13、17、19、23、29、31等。

package main

import "fmt"

func calc(taskChan chan int, resChan chan int, exitChan chan bool) {

    for v := range taskChan {
        flag := true
        for i :=2; i < v; i ++ {
            if v%i==0 {    // 判斷不是素數的內容
                flag = false
                break
            }
        }
        if flag {
            resChan <- v           // 將素數寫入管道
        }
    }
    fmt.Println("exit")
    exitChan <- true     // 結束時將結束標記寫入結束的管道
}




func main() {
    // 定義三個管道,intChan里存1000個數字,在這些數字里判斷素數
    intChan := make(chan int, 1000)
    // resultChan里存判斷出來的素數
    resultChan := make(chan int, 1000)
    // exitChan里存儲進程結束的信號,用來判斷子進程是否都結束了
    exitChan := make(chan bool, 8)
    
    go func() {        // 啟動一個線程往intChan管道里寫入1000個數
        for i :=0; i < 1000; i ++ {
            intChan <- i
        }
        // 1000個數寫完后關閉管道(不能往管道里寫數據了、用for range循環完intChan管道后會自動退出循環)    
        close(intChan)
    }()

    // 啟動8個攜程執行計算素數的函數
    for i :=0; i < 8; i ++ {
        go calc(intChan, resultChan, exitChan)        // calc(數據管道, 結果管道, 退出信號管道)
    }

    // 啟動一個線程,獲取所有計算素數子進程的退出新號(從退出信號管道里獲取八個退出信息號,證明八個goroutine全部退出了)
    go func() {
        for i := 0; i < 8; i ++ {
            <- exitChan      // 只從管道里把內容取出來(不需要獲取)可以這么寫( <- exitChan),不用使用變量接,就是從管道里取出內容丟掉
            fmt.Println("wait goroutine", i, "exited")
        }
        close(resultChan)     // 所有計算素數的goroutine退出后關閉結果管道
    }()

    // 循環讀取計算后得到的素數(管道關掉之后for range循環完畢之后就會自動退出循環)
    for v := range resultChan {
        fmt.Println(v)
    }    

 

1.3、使用管道判斷多個子線程是否結束示例

package main

import (
    "fmt"
)

// 往管道里寫數據的函數
func senddata(ch chan int, exitChan chan struct{}) {
    // 往數據管道里寫數據,寫完后關閉管道
    for i := 0; i < 10; i++ {
        ch <- i
    }
    close(ch)

    // 往數據管道里寫完數據后往退出管道里寫入退出新號
    var a struct{}
    exitChan <- a
}

// 從管道里讀數據的函數
func getdata(ch chan int, exitChan chan struct{}) {
    // 死循環讀取管道里的數據
    for {
        // 使用死循環讀取管道里的數據時一定要通過類型斷言判斷管道關閉
        v, ok := <-ch
        if !ok {
            fmt.Println("form chan get data failed")
            break
        }
        fmt.Println(v)
    }
    // 讀取完數據后往退出管道里寫入退出新號
    var a struct{}
    exitChan <- a
}

func main() {
    // 初始化並實例化數據管道與結束信號管道
    var pipe chan int
    pipe = make(chan int, 10)
    exitChan := make(chan struct{}, 3)

    // 啟動一個往管道里寫數據的子線程
    go senddata(pipe, exitChan)
    // 啟動一個從管道里讀數據的子線程
    go getdata(pipe,exitChan)
    
    // 判斷兩個子線程是否結束
    var total = 0;
    for _ = range exitChan {
        total ++
        if total == 2 {      // 如果exitChan里有兩個數則表示兩個子線程結束了
            break
        }
    }


}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM