Golang中的管道(channel) 、goroutine与channel实现并发、单向管道、select多路复用以及goroutine panic处理

这篇具有很好参考价值的文章主要介绍了Golang中的管道(channel) 、goroutine与channel实现并发、单向管道、select多路复用以及goroutine panic处理。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

Golang中的管道(channel) 、goroutine与channel实现并发、单向管道、select多路复用以及goroutine panic处理

目录

管道(channel)

无缓冲管道

有缓冲管道

需要注意

goroutine与channel实现并发

单向管道

定义单向管道

将双向管道转换为单向管道

单向管道作为函数参数

单向管道的代码示例

select多路复用

案例演示

goroutine panic处理

案例演示


管道(channel)

管道(channel)是 Go 语言中实现并发的一种方式,它可以在多个 goroutine 之间进行通信和数据交换。管道可以看做是一个队列,通过它可以进行先进先出的数据传输,支持并发的读和写。

Go 语言中使用 make 函数来创建一个管道,它的语法如下:

ch := make(chan 数据类型)

其中,数据类型可以是任意的 Go 语言数据类型,例如 int、string 等。创建了一个管道之后,我们就可以在多个 goroutine 之间进行数据传输了。

无缓冲管道

无缓冲管道是指在创建管道时没有指定容量,也就是说,它只能存储一个元素,当一个 goroutine 尝试向管道发送数据时,它会阻塞直到另一个 goroutine 从管道中读取数据。同样的,当一个 goroutine 尝试从管道中读取数据时,它也会阻塞直到另一个 goroutine 向管道中发送数据。

示例代码:

package main

import (
    "fmt"
)

func main() {
    ch := make(chan int)
    go func() {
        ch <- 10
    }()
    fmt.Println(<-ch)
}

创建了一个无缓冲管道 ch,并在一个 goroutine 中向管道中发送了一个整数 10。在主 goroutine 中,我们通过 <-ch 从管道中读取数据并打印出来。

有缓冲管道

有缓冲管道是指在创建管道时指定了容量,这时候它可以存储多个元素,但是当管道已满时,尝试向管道发送数据的 goroutine 会被阻塞,直到另一个 goroutine 从管道中读取数据以腾出空间。同样的,当管道为空时,尝试从管道中读取数据的 goroutine 也会被阻塞,直到另一个 goroutine 向管道中发送数据。

示例代码:

package main

import (
    "fmt"
)

func main() {
    ch := make(chan int, 2)
    ch <- 10
    ch <- 20
    fmt.Println(<-ch)
    fmt.Println(<-ch)
}

创建了一个容量为 2 的有缓冲管道 ch,并在主 goroutine 中向管道中依次发送了整数 1020。接着,我们依次从管道中读取数据并打印出来。

需要注意

1.管道是有缓冲的,可以通过指定缓冲区大小来控制数据在管道中的流动。如果缓冲区已满,写入操作将会阻塞直到缓冲区有空间;如果缓冲区为空,读取操作将会阻塞直到有数据写入。

2.管道的写入和读取操作都是阻塞的,直到操作完成才会返回。如果需要非阻塞的读写操作,可以使用select语句进行多路复用。

3.管道可以被关闭,一旦管道被关闭,读取操作将不再阻塞,返回一个零值和一个标识管道已关闭的错误;写入操作将会抛出 panic。为了避免 panic,可以在写入操作之前先检查管道是否已关闭。

4.管道可以用作信号量或同步器,例如使用一个无缓冲的管道实现多个 goroutine 之间的同步。

goroutine与channel实现并发

下面是一个协程与管道实现并发的代码示例,其中使用了两个管道,一个用于发送整数数据,另一个用于接收处理后的数据:

package main

import (
	"fmt"
)

func produce(out chan<- int) {
	for i := 0; i < 5; i++ {
		out <- i
	}
	close(out)
}

func consume(in <-chan int, out chan<- int) {
	for v := range in {
		out <- v * v
	}
	close(out)
}

func main() {
	ch1 := make(chan int)
	ch2 := make(chan int)

	go produce(ch1)
	go consume(ch1, ch2)

	for v := range ch2 {
		fmt.Println(v)
	}
}

代码分析:

1.使用 make 函数创建了两个整数类型的管道 ch1ch2

2.使用 go 关键字分别启动了函数 produceconsume 的协程,其中函数 produce 向管道 ch1 中发送了整数数据,函数 consume 从管道 ch1 中接收数据进行处理,将处理结果发送到管道 ch2 中。

3.在主协程中,使用 range 关键字从管道 ch2 中循环接收处理结果,并将接收到的数据打印出来。

单向管道

在 Go 语言中,有的时候我们会将管道作为参数在多个任务函数间传递,很多时候我们在不同的任务函数中使用管道都会对其进行限制,比如限制管道在函数中只能发送或者只能接收。

定义单向管道

定义一个单向管道可以使用 channel 类型加上箭头运算符(<-)指定读写方向。

例如,定义一个只能写入字符串的单向管道可以使用以下语句:

var ch chan<- string

定义一个只能读出字符串的单向管道可以使用以下语句:

var ch <-chan string   

将双向管道转换为单向管道

双向管道可以转换为只读或只写的单向管道,例如,将一个双向管道转换为只读的单向管道可以使用以下语句:

var ch chan string
var chRead <-chan string = ch

将一个双向管道转换为只写的单向管道可以使用以下语句:

var ch chan string
var chWrite chan<- string = ch

单向管道作为函数参数

单向管道可以作为函数参数来限制管道的读写方向。例如,以下函数接受只读的单向管道作为参数:

func readData(ch <-chan string) {
    // ...
}

以下函数接受只写的单向管道作为参数:

func writeData(ch chan<- string) {
    // ...
}

单向管道的代码示例

以下是一个使用单向管道的代码示例,该示例将一个双向管道转换为只读和只写的单向管道,并将这些单向管道作为函数参数传递:

package main

import "fmt"

func readData(ch <-chan int) {
    for i := range ch {
        fmt.Println("Read data:", i)
    }
}

func writeData(ch chan<- int) {
    for i := 0; i < 5; i++ {
        ch <- i
    }
    close(ch)
}

func main() {
    ch := make(chan int)
    chRead := (<-chan int)(ch)
    chWrite := (chan<- int)(ch)
    go readData(chRead)
    go writeData(chWrite)
    select {}
}

在上面的代码示例中,定义了一个双向管道 ch,然后将它转换为只读的单向管道 chRead 和只写的单向管道 chWrite,并分别将它们作为 readData 和 writeData 函数的参数传递。在 main 函数中,将 readData 和 writeData 函数放入不同的 goroutine 中运行,以便它们可以并发地读取和写入数据。最后使用 select {} 让主程序保持运行,以便 goroutine 可以继续运行。

select多路复用

在Go语言中,select语句可以用于多路复用I/O操作,其语法结构类似于switch语句。它可以同时监视多个管道的读写操作,并在其中一个通道满足读写条件时执行相应的操作。

select语句的语法如下:

select {
case <-ch1:
    // 处理从 ch1 读取到的数据
case data := <-ch2:
    // 处理从 ch2 读取到的数据
case ch3 <- data:
    // 向 ch3 写入数据
default:
    // 如果没有任何 case 语句满足条件,则执行 default 语句
}

select语句中,每个case分支必须是一个通道操作,要么是从通道中读取数据,要么是向通道中写入数据。其中,default分支是可选的,表示如果没有任何case语句满足条件,则执行default语句。

案例演示

package main

import (
    "fmt"
    "time"
)

func main() {
    ch1 := make(chan int)
    ch2 := make(chan int)

    go func() {
        for i := 1; i <= 5; i++ {
            ch1 <- i
            time.Sleep(time.Second)
        }
    }()

    go func() {
        for i := 1; i <= 5; i++ {
            ch2 <- i * i
            time.Sleep(500 * time.Millisecond)
        }
    }()

    for i := 0; i < 10; i++ {
        select {
        case data := <-ch1:
            fmt.Println("Received from ch1:", data)
        case data := <-ch2:
            fmt.Println("Received from ch2:", data)
        }
    }
}

在这个示例中,我们创建了两个通道ch1ch2,并分别向其中写入一些数据。在主函数中,我们使用select语句监听这两个通道,并在其中一个通道中有数据时输出该数据。由于ch1的写入间隔为1秒,而ch2的写入间隔为500毫秒,因此我们可以看到输出的数据是交替出现的。

goroutine panic处理

panic是Go语言中的一种异常处理机制,它的出现是为了让程序在遇到某些不可控制的情况时,能够快速反应,而不是无限期的等待。panic的用法有两种:一种是在程序中显式地调用panic函数,用于处理特定的异常情况;另一种是在程序运行过程中,由于某些不可控制的原因,程序自动抛出panic异常。

案例演示

// 函数
func sayHello() {
    for i := 0; i < 10; i++ {
        fmt.Println("hello,world")
    }
}

// 问题函数
func test() {
    //这里使用defer + recover
    defer func() { //匿名自执行函数
        if err := recover(); err != nil {
            fmt.Println("test() 发生错误", err)
        }
    }()
    //定义一个map
    var myMap map[int]string
    myMap[0] = "hello"
}

func main() {
    //当两个协程中一个出现问题时,另一个也不会进行操作,可以使用异常处理避免
    go sayHello()
    go test()
    //防止主进程退出这里利用time.Sleep
    time.Sleep(time.Second)
}

输出结果:

Golang中的管道(channel) 、goroutine与channel实现并发、单向管道、select多路复用以及goroutine panic处理文章来源地址https://www.toymoban.com/news/detail-457512.html

到了这里,关于Golang中的管道(channel) 、goroutine与channel实现并发、单向管道、select多路复用以及goroutine panic处理的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • golang学习-channel管道

    1、定义 管道是golang语言提供的goroutine间的通讯方式,channel可以让一个goroutine发送特定的值给另一个goroutine的通讯机制。 管道是引用类型。 golang语言中channel是一种特殊的类型。像一个队列一样,先进先出。 var 变量 chan 元素类型 var ch1 chan int //声明一个传递整型的管道 var

    2024年01月19日
    浏览(49)
  • Golang单元测试与Goroutine详解 | 并发、MPG模式及CPU利用

    深入探讨Golang中单元测试方法及Goroutine的使用。了解并发与并行概念,MPG模式以及CPU相关函数的应用。解决协程并行中的资源竞争问题。

    2024年02月10日
    浏览(44)
  • Golang 并发 Channel的用法

    上面是创建了无缓冲的 channel,一旦有 goroutine 往 channel 发送数据,那么当前的 goroutine 会被阻塞住,直到有其他的 goroutine 消费了 channel 里的数据,才能继续运行。 上面示例中的第二个参数表示 channel 可缓冲数据的容量。只要当前 channel 里的元素总数不大于这个可缓冲容量,

    2024年02月21日
    浏览(51)
  • Golang中的协程(goroutine)

    目录 进程 线程 并发 并行 协程(goroutine)  使用sync.WaitGroup等待协程执行完毕 多协程和多线程         进程就是程序在操作系统中的一次执行过程,是系统进行资源分配和调度的基本单位,进程是一个动态概念,是程序在执行过程中分配和管理资源的基本单位,每一个进程

    2024年02月05日
    浏览(37)
  • Golang Channel详解:安全并发通信与避免死锁方法

    深入了解Golang中的Channel,探讨其线程安全性、类型特性以及避免死锁的方法。学习如何正确初始化、存取数据,关闭Channel以及处理只读只写情况。

    2024年02月10日
    浏览(50)
  • 006 Golang-channel-practice 并发打印字符串

    使用两个goroutine,向标准输出中按顺序交替打印出字母和数字,输出是a1b2c3... 这里我们使用两个channel保证它们打印的顺序。 使用两个通道(channel)是确保打印顺序的一个有效方法,尤其是在涉及到交替执行的并发任务时。在我们的场景中,目标是让两个 goroutine 交替执行并打

    2024年01月18日
    浏览(40)
  • golang发送get请求的各种操作:自定义header请求头、带cookie请求、channel并发请求

    请求参数放到url.Values{} 接收文件–ioutil.WriteFile 接收文件–io.Copy

    2024年02月06日
    浏览(47)
  • Go后端开发 -- goroutine && channel

    多进程操作系统 解决了阻塞的问题 存在切换成本 设计复杂 将一个线程分为用户线程和内核线程,CPU只能看到内核线程 使用协程调度器调度多个协程,形成N:1关系 多个线程管理多个协程,M:N,语言的重点就在于协程调度器的优化 goroutine内存更小,可灵活调度 Golang早期对调

    2024年01月23日
    浏览(48)
  • golang协程goroutine教程

    项目经常遇到一些批量任务执行太慢,需要开启多线程去处理,记录下在 Golang 中协程使用的一些操作。 协程是计算机程序的一类组件,推广了协作式多任务的子例程,允许执行被挂起与被恢复。相对子例程而言,协程更为一般和灵活,但在实践中使用没有子例程那样广泛。

    2024年02月02日
    浏览(41)
  • golang学习-goroutine

    1、goroutine协程 goroutine 是 Go 语言支持并发的核心,一个goroutine会以一个很小的栈开始其生命周期,一般只需要2KB。区别于操作系统线程由系统内核进行调度, goroutine 是由Go运行时(runtime)负责调度。例如Go运行时会智能地将 m个goroutine 合理地分配给n个操作系统线程,实现类

    2024年01月18日
    浏览(41)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包