Go无缓冲通道(同步通道)的实现

 更新时间:2025年02月06日 09:53:07   作者:2301_76723322  
本文主要介绍了Go无缓冲通道(同步通道)的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

无缓冲的通道又称为阻塞的通道,我们来看一下如下代码片段。

package main

import "fmt"

func main() {
    ch := make(chan int)
    ch <- 1
    fmt.Println("发送成功")
}

上面这段代码能够通过编译,但是执行的时候会出现以下错误:

在这里插入图片描述

deadlock表示我们程序中的 goroutine 都被挂起导致程序死锁了。为什么会出现deadlock错误呢?

因为我们使用ch := make(chan int)创建的是无缓冲的通道,无缓冲的通道只有在有接收方能够接收值的时候才能发送成功,否则会一直处于等待发送的阶段

上面的代码会阻塞在ch <- 1这一行代码形成死锁,那如何解决这个问题呢?

其中一种可行的方法是创建一个 goroutine 去接收值,例如:

package main

import (
    "sync"
)

func main() {
    var wg sync.WaitGroup
    wg.Add(1)
    ch := make(chan int)
    ch <- 1
    go func() {
       defer wg.Done()
        v := <-ch
       fmt.Println(v)
    }()
    close(ch)
    wg.Wait()
}

在这里插入图片描述

我们已经开了一个go协程从管道中读去数据了,为什么还会报错呢?

因为当程序执行到 ch <- 1时,进已经发生了阻塞,下面的go协程还没有来得及启动。
go的channel在执行写入时会先检查在此之前有没有读取数据的一方已经存在,在读取时会先检查在此之前有没有写入方已经存在。

当我们将读的协程先启动,再写入,就可以了,代码如下:

package main

import (
    "fmt"
    "sync"
)

func main() {
    var wg sync.WaitGroup
    wg.Add(1)
    ch := make(chan int)
    go func() {
       defer wg.Done()
       v := <-ch
       fmt.Println(v)
    }()
    ch <- 1
    close(ch)
    wg.Wait()
}

在这里插入图片描述

同理,如果对一个无缓冲通道执行接收操作时,没有任何向通道中发送值的操作那么也会导致接收操作阻塞

package main

import "fmt"

func main() {
    ch := make(chan int)
    <-ch
    fmt.Println("接收成功")
}

在这里插入图片描述

其中一种可行的方法是创建一个 goroutine 去写入值,例如:

package main

import (
    "fmt"
    "sync"
)

func main() {
    var wg sync.WaitGroup
    wg.Add(1)
    ch := make(chan int)
    v := <-ch
    go func() {
       defer wg.Done()
       ch <- 1
    }()
    fmt.Println(v)
    close(ch)
    wg.Wait()
}

在这里插入图片描述

同理,因为当程序执行到 v := <-ch 时,进已经发生了阻塞,下面的go协程还没有来得及启动。
当我们将写的协程先启动,再读取,就可以了,代码如下:

package main

import (
    "fmt"
    "sync"
)

func main() {
    var wg sync.WaitGroup
    wg.Add(1)
    ch := make(chan int)
    go func() {
       defer wg.Done()
       ch <- 1
    }()
    v := <-ch
    fmt.Println(v)
    close(ch)
    wg.Wait()
}

在这里插入图片描述

使用无缓冲通道进行通信将导致发送和接收的 goroutine 同步化。因此,无缓冲通道也被称为同步通道
同步:两个goroutine1(写入方)、goroutine2(读取方),
goroutine1先执行,如果想要再次发送(写入)数据的话,必须等待goroutine2将channel中的数据取出来(读取)之后,才能再次发送。
goroutine2先执行,如果想要再次接收数据的话,必须等待goroutine1再次向channel中写入数据之后,才能再次接收。
执行顺序(goroutine1,goroutine2,goroutine1,goroutine2…)goroutine1和goroutine2交替执行。
示例演示:使用一个无缓冲channel和两个goroutine实现交替打印一个字符串。

package main

import "fmt"

func main() {
    ch := make(chan int)

    str := "hello, world"

    go func() {
       for {
          index, ok := <-ch
          if !ok {
             break
          }
          if index >= len(str) {
             close(ch)
             break
          }
          fmt.Printf("Goroutine1 : %c\n", str[index])
          ch <- index + 1
       }
    }()

    ch <- 0

    for {
       index, ok := <-ch
       if !ok {
          break
       }
       if index >= len(str) {
          close(ch)
          break
       }
       fmt.Printf("Goroutine1 : %c\n", str[index])
       ch <- index + 1
    }

}

在这里插入图片描述

到此这篇关于Go无缓冲通道(同步通道)的实现的文章就介绍到这了,更多相关Go无缓冲通道内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家! 

相关文章

  • B站新一代 golang规则引擎gengine基础语法

    B站新一代 golang规则引擎gengine基础语法

    这篇文章主要为大家介绍了B站新一代 golang规则引擎gengine基础语法,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-12-12
  • Golang中Gin数据库表名前缀的三种方法

    Golang中Gin数据库表名前缀的三种方法

    本文主要介绍了Golang中Gin数据库表名前缀的三种方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2025-02-02
  • 揭秘Go语言中的反射机制

    揭秘Go语言中的反射机制

    在Go语言中,反射是通过reflect包来实现的,通过使用反射,我们可以在运行时获取对象的类型信息、访问对象的字段和方法、动态调用方法等,反射在很多场景下都非常有用,比如编写通用的代码、实现对象的序列化和反序列化、实现依赖注入等,需要的朋友可以参考下
    2023-10-10
  • golang中的并发和并行

    golang中的并发和并行

    这篇文章主要介绍了golang中的并发和并行用法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2021-05-05
  • 一文彻底理解Golang闭包实现原理

    一文彻底理解Golang闭包实现原理

    闭包对于一个长期写Java的开发者来说估计鲜有耳闻,光这名字感觉就有点"神秘莫测"。这篇文章的主要目的就是从编译器的角度来分析闭包,彻底搞懂闭包的实现原理,需要的可以参考一下
    2022-10-10
  • Golang标准库time包日常用法小结

    Golang标准库time包日常用法小结

    本文主要介绍了Golang标准库time包日常用法小结,可以通过它们来获取当前时间、创建指定时间、解析时间字符串、控制时间间隔等操作,感兴趣的可以了解一下
    2023-11-11
  • 谈谈golang的netpoll原理解析

    谈谈golang的netpoll原理解析

    本文详细介绍了Go语言中netpoll部分的实现细节和协程阻塞调度原理,特别是epoll在Linux环境下的工作原理,Go语言通过将epoll操作放在runtime包中,结合运行时调度功能,实现了高效的协程I/O操作,感兴趣的朋友跟随小编一起看看吧
    2024-11-11
  • 利用Go语言搭建WebSocket服务端方法示例

    利用Go语言搭建WebSocket服务端方法示例

    这篇文章主要给大家介绍了利用Go语言搭建WebSocket服务端方法,文中通过示例代码介绍的非常详细,需要的朋友们可以参考借鉴,下面来一起看看吧。
    2017-04-04
  • 使用Go调用第三方API的方法详解

    使用Go调用第三方API的方法详解

    在现代应用开发中,调用第三方API是非常常见的场景,比如获取天气预报、翻译文本、发送短信等,Go作为一门高效并发的编程语言,拥有强大的标准库和丰富的第三方库,可以非常方便地与外部API进行交互,本文将通过两个实战案例,带你掌握如何在Go中调用第三方 API
    2025-09-09
  • golang切片反序实例

    golang切片反序实例

    这篇文章主要介绍了golang切片反序实例,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-12-12

最新评论