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

 更新时间:2023年05月25日 08:21:28   作者:JetTsang  
go 当中的并发编程是通过goroutine来实现的,利用channel(管道)可以在协程之间传递数据,所以本文就来讲讲Golang如何利用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 
}

到此这篇关于Golang利用channel协调协程的方法详解的文章就介绍到这了,更多相关Golang channel协调协程内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 浅析GO语言的垃圾回收机制

    浅析GO语言的垃圾回收机制

    今天我们来聊聊golang是如何进行垃圾回收的,我们知道,目前各语言进行垃圾回收的方法有很多,如引用计数、标记清除、分代回收、三色标记等,各种方式都有其特点,文中介绍的非常详细,感兴趣的小伙伴跟着小编一起学习吧
    2023-07-07
  • golang用melody搭建轻量的websocket服务的示例代码

    golang用melody搭建轻量的websocket服务的示例代码

    在Go中,可以使用gin和melody库来搭建一个轻量级的WebSocket服务,gin是一个流行的Web框架,而melody是一个用于处理WebSocket的库,本文给大家演示如何使用gin和melody搭建WebSocket服务,感兴趣的朋友一起看看吧
    2023-10-10
  • go select编译期的优化处理逻辑使用场景分析

    go select编译期的优化处理逻辑使用场景分析

    select 是 Go 中的一个控制结构,类似于用于通信的 switch 语句。每个 case 必须是一个通信操作,要么是发送要么是接收。接下来通过本文给大家介绍go select编译期的优化处理逻辑使用场景分析,感兴趣的朋友一起看看吧
    2021-06-06
  • Golang实现AES对称加密算法实例详解

    Golang实现AES对称加密算法实例详解

    所谓对称加密是指在加密和解码时使用同一密钥的加密方式,下面这篇文章主要给大家介绍了关于Golang实现AES对称加密算法的相关资料,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2023-02-02
  • Go 实现 WebSockets和什么是 WebSockets

    Go 实现 WebSockets和什么是 WebSockets

    这篇文章主要介绍了Go 实现 WebSockets和什么是 WebSockets,WebSockets 是构建实时应用程序的第一大解决方案,在线游戏、即时通讯、跟踪应用程序等,下文相关内容介绍需要的小伙伴可以参考一下
    2022-04-04
  • 详解Go中gin框架如何实现带颜色日志

    详解Go中gin框架如何实现带颜色日志

    当我们在终端上(比如Goland)运行gin框架搭建的服务时,会发现输出的日志是可以带颜色的,那这是如何实现的呢?本文就来和大家简单讲讲
    2023-04-04
  • 为什么GO不支持循环引用

    为什么GO不支持循环引用

    这篇文章主要介绍的是为什么GO不支持循环引用,学习 Go 语言的开发者越来越多了,很多小伙伴在使用时,就会遇到种种不理解的问题,其中一点就是包的循环引用的报错,下main文章我们一起来看看学习原因
    2021-10-10
  • GO使用阿里云,解决go get下载项目慢或无法下载的情况

    GO使用阿里云,解决go get下载项目慢或无法下载的情况

    这篇文章主要介绍了GO使用阿里云,解决go get下载项目慢或无法下载的情况,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-01-01
  • Go语言interface详解

    Go语言interface详解

    这篇文章主要介绍了Go语言interface详解,本文讲解了什么是interface、interface类型、interface值、空interface、interface函数参数等内容,需要的朋友可以参考下
    2014-10-10
  • Golang实现Redis事务深入探究

    Golang实现Redis事务深入探究

    这篇文章主要介绍了Golang实现Redis事务深入探究,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2024-01-01

最新评论