go语言开发基础39 - 之go语言里的管道(channel)基本使用

  • go语言里的管道(channel)类似unix中的管道(pipe)。
  • channel里的数据遵循先进先出的原则。
  • 不加锁多个goroutine同时操作管道里的数据时不会出现数据冲突。
  • channel是有类型的。

 

channel定义与操作

1、int类型的管道定义、初始化与写和读数据

package main

import "fmt"

func main() {
    var intChan chan int        // 定义int类型的管道,chan int为管道的类型
    intChan = make(chan int, 4)       // 初始化管道的长度
    intChan <- 10          // 往管道里写入数据
    num := <-intChan          // 从管道里读取数据
    fmt.Println(num)          // 结果为:10
}

 

2、字符串类型的管道定义、初始化与写和读操作

package main

import "fmt"

func main() {
    var strChan chan string       // 定义字符串类型的管道
    strChan = make(chan string, 4)        // 初始化管道长度
    intChan <- "is string"          // 往管道里写入数据
    str := <-intChan         // 从管道里读取数据
    fmt.Println(str)      // 结果为:is string
}

 

3、map类型的管道定义、初始化与写和读操作

package main

import "fmt"

func main() {
    var mapChan chan map[string]string
    mapChan = make(chan map[string]string, 4)
    m := make(map[string]string)
    m["stu01"] = "001"
    m["stu02"] = "002"
    mapChan <- m // 将数据写入管道

    outm := <-mapChan // 从管道获取数据
    fmt.Println(outm)
}

 

4、自定义结构体类型的管道定义、初始化与写和读操作

package main

import "fmt"

type Student struct {
    Name string
}


func main() {
    var stuChan chan *Student
    stuChan = make(chan *Student, 10)
    stu := Student{Name: "stu01"}
    stuChan <- &stu      // 写入变量的地址

    outstu := <-stuChan  // 读取内容(读取的也是变量的地址)
    fmt.Println(*outstu)     // 打印时需要加*号获取值,否则获取到的是地址
}

 

5、interface类型的管道定义、初始化、写、读操作与类型转换

package main

import "fmt"

type Student struct {
    Name string
}

func main() {
    var stuChan chan interface{}
    stuChan = make(chan interface{}, 10)

    stu := Student{Name: "interstu"}
    stuChan <- &stu

    var stu01 interface{}
    stu01 = <-stuChan
    fmt.Printf("stu01: type[%T],data[%s]\n", stu01, stu01)

    // 类型转换(将interface类型的变量stu01转换为Student类型的变量stu02)
    var stu02 *Student
    // 类型断言判断是否可以转换
    stu02, ok := stu01.(*Student)
    if !ok {        // 如果不可转换打印错误信息
        fmt.Println("con not convert", ok)
        return
    }
    fmt.Printf("stu02: type[%T],data[%s]\n", stu02, stu02)     // 打印转换结果
}

 

二、管道关闭后的写和读操作

2.1、管道关闭后再执行写操作时会报错示例

package main

func main() {
    var wchan chan int      // 定义管道并初始化管道长度
    wchan = make(chan int, 5)
    wchan <-10         // 网管到里写入数据
    close(wchan)       // 关闭管道
    wchan <-11      // 关闭管道后再执行写入数据操作时会报错:panic: send on closed channel
}

 

2.2、管道关闭后在执行循环读取操作

示例一:使用for{}死循环读管道里的数据

package main

import "fmt"

func main() {
    var ch chan int       // 定义并初始化管道
    ch = make(chan int, 10)

    // 循环往管道里写入数据
    for i := 0; i < 10; i++ {
        ch <- i
    }

    // 关闭管道
    close(ch)

    // 使用死循环读取已关闭管道里的数据 --> 如果不做判断会陷入死循环
    for {
        // 通过类型断言判断管道是否关闭(ok的值为true则表示管道没关闭了,如果值为false就表示管道关闭了)
        b, ok := <-ch
        if ok == false {     // 如果不做这个判断ch关闭后再做死循环读取操作时会出现死循环
            fmt.Println("chan is close")
            break
        }
        fmt.Println(b)
    }
}

 

示例二:使用for range循环读取管道里的数据

package main

import "fmt"

func main() {
    // 定义管道并初始化
    var ch chan int
    ch = make(chan int,100)

    // 往管到里写入数据
    for i := 0; i < 100; i ++ {
        ch <- i
    }

    close(ch)       // 往管道里写入数据后关闭管道(如果没有关闭管道,循环完了会陷入等待)

    for v := range ch {       // for range循环完管道后循环自动结束(如果管道没关闭,循环完了会等待)
        fmt.Println(v)
    }
}

 

三、通过类型断言判断管道关闭了没

package main

import "fmt"

func main() {
    // 定义与初始化管道
    var ch chan int
    ch = make(chan int,100)

    // 管道里写入数据
    for i := 0; i < 100; i ++ {
        ch <- i
    }
    close(ch)       // 往管道里写入数据后关闭管道
    for {
        var b int
        b, ok := <- ch     // 判断一个管道是否关闭了(如果ok的值是false就表示管道关闭了)
        if ok == false {
            fmt.Println("channel is close")
            break
        }
        fmt.Println(b)
    }
}

 

四、只读管道和只写管道

只读管道:var readChan <-chan string   函数参数:func recvdata(ch <-chan int){}
只写管道:var writeChan chan<- string  函数参数:func adddata(ch chan<- int) {}
package main

import (
    "fmt"
)

// 往管道里写数据的函数
func adddata(ch chan<- int, exitChan chan struct{}) {       // ch chan<- int:接收只写类型的管道
    for i := 0; i < 10; i++ {
        ch <- i
    }
    close(ch)

    var a struct{}
    exitChan <- a
}

// 从管道里读取数据的函数
func recvdata(ch <-chan int, exitChan chan struct{}) {      // ch <-chan int:接收只读类型参数
    for {
        v, ok := <-ch
        if !ok {
            // fmt.Println("form chan get data failed")
            break
        }
        fmt.Println("recvdata",v)
    }
    var a struct{}
    exitChan <- a
}

func main() {
    // 定义数据管道和退出管道并初始化管道长度
    var pipe chan int
    pipe = make(chan int, 10)
    exitChan := make(chan struct{}, 3)

    // 启动两个子进程执行写数据和读数据函数
    go adddata(pipe, exitChan)
    go recvdata(pipe,exitChan)
    
    // 判断写数据和读数据的子进程是否结束
    var total = 0;
    for _ = range exitChan {
        total ++
        if total == 2 {
            break
        }
    }


}

 

五、使用select读取管道里的数据

package main

import (
    "fmt"
    "time"
)

func main() {
    // 定义两个管道并初始化
    var ch chan int
    ch = make(chan int, 10)
    ch2 := make(chan int, 10)

    // 往两个管道里写数据
    go func() {
        for i := 0; i < 30; i ++ {
            ch <- i
            ch2 <- i*i
        }
    }()

    // 使用死循环加select读数据
    for {
        select {
        case v := <- ch:
            fmt.Println(v)
        case v := <- ch2:
            fmt.Println(v)
        default:
            fmt.Println("default info")
            time.Sleep(time.Second)
        }
    }
}

 

 
posted @ 2020-08-04 16:51  欧-阳  阅读(624)  评论(0)    收藏  举报