开发者

GO利用channel协调协程的实现

开发者 https://www.devze.com 2023-11-21 10:39 出处:网络 作者: JetTsang
目录前言使用实现生产者消费者模式实战面试题: 「交替打印数字和字母」题目解题思路结尾前言
目录
  • 前言
  • 使用
  • 实现生产者消费者模式
  • 实战面试题: 「交替打印数字和字母」
    • 题目
    • 解题思路
  • 结尾

    前言

    go 当中的并发编程是通过goroutine来实现的,利用channel(管道)可以在协程之间传递数据,实现协程的协调与同步。

    使用

    新建一个管道,使用make channel 来构建

    // 构建一个缓存长度为8 的管道
    ch := make(chan int ,8)
    // 写入
    ch <- 10
    // 取出
    number := <-ch
    // 关闭
    close(ch)

    注意⚠️: 取数据的时候,如果没得取,会阻塞代码的执行,如果一直没有取到,那就是死锁

    实现生产者消费者模式

    两个生产者者协程和一个消费者协程

    使用waitGroup

    fuphpnc main() { 
      ch := make(chan int, 100) 
      wg := sync.WaitGroup{} 
      wg.Add(2) 
      // 生产者
      go fujsnc() { 
        defer wg.Done() 
        // 写入数据 
        for i := 0; i < 10; i++ { 
          ch <- i 
        } 
      }() 
      // 生产者
      go func() { 
        defer wg.Done() 
        // 写入数据 
        for i := 0; i < 10; i++ { 
          ch <- i 
        } 
      }() 
      wg2 := sync.WaitGroup{} 
      wg2.Add(1) 
      // 消费者
      go func() { 
        sum := 0 
        fmt.Printf("sum %d \n", sum) 
        for { 
          // 这里会等待 
          temp, ok := <-ch 
          // close 并且 管道为空,ok = false 
          if !ok {  
            break 
          } else { 
            sum += temp 
          } 
        } 
        fmt.Printf("sum %d \n", sum) 
        wg2.Done() 
      }() 
      // 等待俩生产者结束 
      wg.Wait() 
      // 生产数据之后,消费者也并行读完了,此时可以关闭 管道 来 跳出for循环了
      close(ch) 
      // 等待消费者协程结束 
      wg2.Wait() 
    }

    使用管道则将wg2相关的代码改掉

    func main() { 
      //...
      //...
      ch2 := make(chan struct{}, 0) 
      go func() { 
        sum := 0 
        fmt.Printf("sum %d \n", sum) 
        for { 
          // 这里会等待 
          temp, ok := <- ch 
          // close 并且 管道为空,ok = false 
          if !ok { 
            break 
          } else { 
            sum += temp 
          } 
        } 
        fmt.Printf("sum %d \n", sum) 
        ch2 <- struct{}{} 
      }() 
      // 等待俩生产者结束 
      wg.Wait() 
      // 关闭管道
      close(ch) 
      // 等待消费者协程结束 
      <-ch2
    }

    实战面试题: 「交替打印数字和字母」

    题目

    使用两个 goroutine 交替打印序列,一个 goroutine 打印数字, 另外一个 goroutine 打印字母, 最终效果如下:

    12AB34CD56EF78GH910IJ1112KL1314MN1516OP1718QR1920ST2122UV2324WX2526YZ2728

    解题思路

    利用channel的 阻塞 来协调线程,达到线程交叉执行的效果。

    代码

    func main() {  
        letter, number := make(chan bool), make(chan bool)  
        wait := sync.WaitGroup{}  
        go func() {  
            i := 1  
            for {  
                if <-number {  
                    fmt.Print(i)  
                    i++  
                    fmt.Print(i)  
                    i++  
                    letter <- true  
                }  
            }  
        }()  
        wait.Add(1)  
        go func() {  
            // 获得ASCII码  
            i := 'A'  
            for {  
                if <-letter {  
                    // 当前已经超过Z时,无需再打印
                    if i > 'Z' {  
                        // 停止等待,并且跳出循环
                        wait.Done()  
                        break  
                    }  
                    // 将ASCII码强转成字母输出  
                    fmt.Print(string(i))  
                    i++  
                    fmt.Print(string(i))  
                    i++  
                    number <- true  
                }  
            }  
        }()  
        // 放行数字打印的阻塞 
        number <- true  
        // 等待关闭主线程
        wait.Wait()  
    }

    其实完全也可以将waitGroup换成管道

    func main() {  
        letter, number := make(chan bool), make(ch编程an bool) 编程客栈 
        // 再定义一个管道来等待,替代waitGroup的作用
        wait := make(chan bool)
        // 打印数字
        go func() {  
            i := 1  
            for {  
                if <-number {  
                    fmt.Print(i)  
                    i++  
                    fmt.Print(i)  
                    i++  
                    letter <- true  
                }  
            }  
        }()  
        // 打印字母
        go func() {  
            // 获得ASCII码  
            i := 'A'  
            for {  
                if <-letter {  
                    if i > 'Z' {  
                        wait <- true  
                        break  
                    }  
                    // 将ASCII码强转成字母输出  
                    fmt.Print(string(i))  
                    i++  
                    fmt.Print(string(i))  
                    i++  
                    number <- true  
                }  
            }  
     编程客栈   }()  
        number <- true  
        // 等待管道取值
        <- wait 
    }

    结尾

    到此这篇关于GO利用channel协调协程的实现的文章就介绍到这了,更多相关GO  channel协调协程内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

    0

    精彩评论

    暂无评论...
    验证码 换一张
    取 消

    关注公众号