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