7.基础数据结构与算法

予早 2024-12-21 09:00:11
Categories: Tags:

sort包

管道,channel

Go推荐使用channel进行通信,原则,不使用共享内存进行通信,而基于通信共享内存

管道本质上是线程安全的队列,管道是引用类型。

通道可用于两个 goroutine 之间通过传递一个指定类型的值

读写

ch := make(chan int, 100)   //双向通道
ch := make(<-chan int, 100) //只读通道
ch := make(chan<- int, 100) //只写通道
  1. 发送方向管道发送一条数据
    1. 无缓冲区时,发送方阻塞,必须等到接收方接收该数据阻塞结束
    2. 有缓冲区时
      1. 缓冲区满,发送方阻塞,必须等到接收方从缓冲区获取一条数据,然后发送方才能将要发送的数据放到缓冲区,随后阻塞结束
      2. 缓冲区未满,发送方将数据放置缓冲区,不阻塞
  2. 接收方从管道接收一条数据
    1. 无缓冲区时,接收方阻塞,必须等到发送方发送一条数据
    2. 有缓冲区时
      1. 缓冲区空,接收方阻塞,必须等到发送方发送一条数据,然后接收方才能从缓冲区获取一条数据,随后阻塞结束
      2. 缓冲区非空,接收方从缓冲区获取一条数据,不阻塞

基于上述特点可以实现:

package main

import (
    "fmt"
)

func main() {
    intChannel := make(chan int, 3)

    fmt.Printf("%v %v %v\n", intChannel, len(intChannel), cap(intChannel))

    intChannel <- 1
    intChannel <- 2
    intChannel <- 3
    //intChannel <- 4
    //0xc000078100fatal error: all goroutines are asleep - deadlock!
    //放不进去会阻塞,Go检测到无法解除阻塞,所以直接报错

    var num int
    var ok bool

    num, ok = <-intChannel

    fmt.Println(num, ok)

    num = <-intChannel //可以只取值

    <-intChannel //取出但不存储
    //num = <-intChannel
    //fatal error: all goroutines are asleep - deadlock!
    //取不出来会阻塞,Go检测到无法解除阻塞,所以直接报错

    close(intChannel)

    num, ok = <-intChannel
    fmt.Println(num, ok)

}

遍历管道

func close
关闭管道,通道必须可写,应当由发送者执行,在最后的值从已关闭的信道中被接受后,任何对其读操作都会无阻塞成功
x, ok := <-channel
没有数据时,x为对应类型默认值,ok为false

package main

import "fmt"

func main() {
    intChannel := make(chan int, 10)

    for i := 0; i < 10; i++ {
        intChannel <- i
    }
    close(intChannel)

    for i := range intChannel {
        fmt.Println(i)
    }

    fmt.Printf("%v %v %v\n", intChannel, len(intChannel), cap(intChannel))
}

将双向管道赋值给一个单向管道类型变量,达到控制访问目的

只读只写,将一个双向的管道作为只写管道传入函数,防止函数内部修改

package main

func main() {

    var intChannelOnlySend chan<- int

    intChannelOnlySend = make(chan int, 10)

    for i := 0; i < 5; i++ {
        intChannelOnlySend <- i
    }

    //<-intChannel //invalid operation: cannot receive from send-only channel intChannel (variable of type chan<- int)

    var intChannelOnlyReceive <-chan int

    intChannelOnlyReceive = make(chan int, 10)

    <-intChannelOnlyReceive

}

select多路复用

package main

import (
    "fmt"
    "time"
)

func main() {
    // 一个int管道
    var intChannel chan int
    intChannel = make(chan int, 10)

    // 每隔1s向int管道放一个1
    go func() {
        for {
            time.Sleep(time.Second)
            intChannel <- 1
        }
    }()

    // 一个string管道
    stringChannel := make(chan string, 10)

    // 每秒向string管道放一个string
    go func() {
        for {
            time.Sleep(time.Second)
            stringChannel <- "string"
        }
    }()

    // 管道select多路复用
    for {
        time.Sleep(time.Second)
        select {
        case v := <-intChannel:
            fmt.Println(v)
        case v := <-stringChannel:
            fmt.Println(v)
        default:
            fmt.Println("nil")
        }
    }

}

多路复用停止

package main

import "fmt"

func fibonacci(c, quit chan int) {
    x, y := 0, 1
    for {
        select {
        case c <- x:
            x, y = y, x+y
        case <-quit:
            fmt.Println("quit")
            return
        }
    }
}

func main() {
    c := make(chan int)
    quit := make(chan int)

    go func() {
        for i := 0; i < 10; i++ {
            fmt.Println(<-c)
        }
        quit <- 0
    }()
    fibonacci(c, quit)
}

由于Go除内置类型之外没有额外支持数据类型,故需要三方库支持。

Go数据结构与算法库推荐:

https://awesome-go.com/data-structure-and-algorithm-collections/

GoDS: