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

news2025/1/18 11:54:09

目录

管道(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)
}

输出结果:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/556691.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

APP服务端架构的演变

大家好&#xff0c;我是易安&#xff01; 早期2013年的时候&#xff0c;随着智能设备的普及和移动互联网的发展&#xff0c;移动端逐渐成为用户的新入口&#xff0c;各个电商平台都开始聚焦移动端App&#xff0c;如今经历了10年的发展&#xff0c;很多电商APP早已经没入历史的洪…

日语文法PPT截图31-45

31 形式名词 とき ところ 作为形式名词的话&#xff0c;一般是要写假名不写汉字的 相对时态 如果是一般时/将来时とき&#xff0c;就是先做后面的动作&#xff0c;在做前面的动作。 出教室的时候&#xff0c;关灯。 如果是过去时とき那么&#xff0c;是先做前面的动作&#…

Linux安装elk

稍后补充。 目录 01【安装elk】 es单机 es集群 esHead插件 kibana logstash elastic search:https://www.elastic.co/cn/downloads/elasticsearchlogstash:https://www.elastic.co/cn/downloads/logstashkibana:https://www.elastic.co/cn/downloads/kibana linux下安装E…

vector的介绍

vector的介绍&#xff1a;(vector翻译是向量&#xff0c;但是表示的是顺序表) vector是表示可以改变大小的数组的序列容器。 就像数组一样&#xff0c;vector对其元素使用连续的存储位置&#xff0c;这意味着也可以使用指向其元素的常规指针上的偏移量来访问它们的元素&#xf…

前端代码规范配置

前端代码规范配置 涉及到了eslint、prettier、husky、lint-staged等工具包的使用。 代码规则校验 使用eslint定义代码风格 安装eslint并在.eslintrc.js文件中配置。 npm i eslint -D这个代码风格可以使用公司团队内的规范&#xff0c;如果没有可以在github中找到一些主流的…

主机访问不到虚拟机(centos7)web服务的解决办法

目录 一、背景 二、解决办法 2.1、配置虚拟机防火墙 2.2、修改虚拟机网络编辑器 一、背景 主机可以访问外网&#xff0c;虚拟机使用命令&#xff1a;curl http://网址&#xff0c;可以访问到web服务 &#xff0c;主机使用http://网址&#xff0c;访问不到虚拟机&#xff08…

TikTok掀动出海淘金潮

嘉晟迪科&#xff1a;在各行各业都已经卷成红海的今天&#xff0c;最稀缺的是什么&#xff1f;当然是增长。那么&#xff0c;增长在哪里&#xff1f;流量在哪里&#xff0c;需求就在哪里&#xff0c;增长也就在那里。 因为短视频风靡全球的流行&#xff0c;内容平台特别是短视频…

Python-web开发学习笔记(2)--- HTML基础

先回顾一下上一篇文章&#xff1a;Python-web开发学习笔记&#xff08;1&#xff09;--- HTML基础_python web开发笔记_尚拙谨言的博客-CSDN博客 中讲了哪几个常用的HTML标签&#xff1a; <head>&#xff1a;头声明 <title>&#xff1a;网页标题 <h1>~<h6…

QT入门看这一篇就够了——超详细讲解(40000多字详细讲解,涵盖qt大量知识)

目录 一、Qt概述 1.1 什么是Qt 1.2 Qt的发展史 1.3 Qt的优势 1.4 Qt版本 1.5 成功案例 二、创建Qt项目 2.1 使用向导创建 2.2 一个最简单的Qt应用程序 2.2.1 main函数中 2.2.2 类头文件 2.3 .pro文件 2.4 命名规范 2.5 QtCreator常用快捷键 三、Qt按钮小程序 …

深度学习实战四:全连接神经网络(基于Pytorch,含数据和详细注释)

文章目录 概念softmax与交叉熵反向传播计算机视觉工具包torchvision全连接神经网络实现多分类概念 神经网络的第一层为输入层,最后一层为输出层,中间的所有层都叫做隐藏层 在计算神经网络层数时,一般不计算输入层,比如: 该神经网络的层数为2。输入层神经元有3个,隐藏层…

redis缓存数据库的使用

一&#xff0c;什么是redis &#xff1f;为什么要用它&#xff1f; 简单介绍&#xff1a; Redis是开源的key-value缓存框架&#xff0c;由c语言编写&#xff0c;也是一款高性能的框架提供多种语言的API 。 SET 每秒11万次 取get每秒81000次。 数据完全存储在内存空间中&…

【C++从0到王者】第八站:模板初阶

文章目录 一、泛型编程二、函数模板1.函数模板概念2.函数模板格式3.函数模板的原理4.函数模板的实例化1.隐式实例化2.显示实例化 5.模板参数的匹配原则 三、类模板1.类模板的格式2.类模板的实例化 一、泛型编程 当我们在写一个交换程序的时候 按照我们之前的想法&#xff0c;我…

谷歌算法快讯0519:近日排名变化频繁,排名或许回温?

从上周末到现在&#xff0c;已经有人注意到排名似乎又有了新的变化&#xff0c;根据WebMaster World上的帖子[1]和业内大家的讨论来看&#xff0c;大家共同的认识是5月16日开始就已经有变化&#xff0c;并且在5月19日的SEMRush Sensor来看已经到达峰值。 有一些在3月份谷歌更新…

yomichan使用笔记

导入词典词典下载 键盘快捷键 Alt Insert 打开搜索页面。 Alt DeleteToggle 打开/关闭扩展。 搜索结果中提供以下快捷方式&#xff1a; Esc取消当前搜索。 Alt PgUpPage 向上浏览结果。 Alt PgDnPage 向下浏览结果。 Alt End 转到最后一个结果。 Alt Home 转到第…

zookeeper的安装使用

zookeeper的安装使用 一、下载安装 https://zookeeper.apache.org/ 点击 download 以我自己的安装为例,linux,3.8.0 准备3台linux服务器&#xff1a;192.168.10.128、192.168.10.129、192.168.10.130 1.上传解压 把apache-zookeeper-3.8.0-bin.tar.gz 上传到 /usr/local/zo…

力扣sql中等篇练习(二十五)

力扣sql中等篇练习(二十五) 1 最繁忙的机场 1.1 题目内容 1.1.1 基本题目信息 1.1.2 示例输入输出 a 示例输入 b 示例输出 1.2 示例sql语句 # Write your MySQL query statement below WITH T as (SELECT t.airport_id,SUM(n) numFROM(SELECT departure_airport airport_i…

【C++进阶之路】内存管理

文章目录 一.内存管理1. 内存布局2. C的内存管理 ①内置类型② 自定义类型 3. operate new 与 operate delete ① 解读operate new源代码② 解读operate delete源代码 4. new和delete的基本原理①new对类对象②delete对类对象 拓展—— 深入理解delete[]和new[]对比 C和C内存…

Java数据类型之字符串

字符集/编码表 ex&#xff1a;ASCII码 字符char&#xff1a;单引号‘ ’引起来的单个字符 转义字符 \n 作用&#xff1a;换行&#xff0c;单引号引用&#xff01;&#xff01;&#xff01; 制表符 \t 作用&#xff1a;加上 \t 前面一共空8个 字符与编码的转换 1.直接输出字…

opencv_c++学习(十九)

一、图像间的距离变换 三种常用的距离计算方法&#xff1a; 欧式距离这里就不在解释。 街区距离&#xff1a;顾名思义&#xff0c;就类似于城市距离一样&#xff0c;并不是通过两点间的距离&#xff0c;而是我们从一个地点到达另一个地点的路程(横纵坐标差值之和)。 棋盘距离…

每日学术速递5.21

CV - 计算机视觉 | ML - 机器学习 | RL - 强化学习 | NLP 自然语言处理 Subjects: cs.CV 1.Going Denser with Open-Vocabulary Part Segmenta 标题&#xff1a;通过开放式词汇部分分割变得更密集 作者&#xff1a;Peize Sun, Shoufa Chen, Chenchen Zhu, Fanyi Xiao, Pi…