网络编程
位置:首页>> 网络编程>> Go语言>> Golang利用channel协调协程的方法详解

Golang利用channel协调协程的方法详解

作者:JetTsang  发布时间:2024-05-08 10:21:54 

标签:Go,channel,协程

前言

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

使用

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

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

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

实现生产者消费者模式

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

使用waitGroup

func main() {  
   ch := make(chan int, 100)  
   wg := sync.WaitGroup{}  
   wg.Add(2)  
   // 生产者
   go func() {  
       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(chan 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
}

来源:https://juejin.cn/post/7236670763271798842

0
投稿

猜你喜欢

手机版 网络编程 asp之家 www.aspxhome.com