GO学习之 通道(nil Channel妙用)

GO系列

1、GO学习之Hello World
2、GO学习之入门语法
3、GO学习之切片操作
4、GO学习之 Map 操作
5、GO学习之 结构体 操作
6、GO学习之 通道(Channel)
7、GO学习之 多线程(goroutine)
8、GO学习之 函数(Function)
9、GO学习之 接口(Interface)
10、GO学习之 网络通信(Net/Http)
11、GO学习之 微框架(Gin)
12、GO学习之 数据库(mysql)
13、GO学习之 数据库(Redis)
14、GO学习之 搜索引擎(ElasticSearch)
15、GO学习之 消息队列(Kafka)
16、GO学习之 远程过程调用(RPC)
17、GO学习之 goroutine的调度原理
18、GO学习之 通道(nil Channel妙用)

文章目录

  • GO系列
  • 前言
  • 一、nil channel读写阻塞
  • 二、nil channel 妙用
    • 2.1 一个普通示例
    • 2.2 示例运行分析
    • 2.3 如何妙用 nil channel
  • 三、总结

前言

按照公司目前的任务,go 学习是必经之路了,虽然行业卷,不过技多不压身,依旧努力!!!
在《GO学习之 通道(Channel)》篇中,主要对 Channel 做了简介和用法,主要包含 无缓存通道、有缓存通道 和 单向通道 等,此篇补充一个 nil channel的用法,
看 nil channel 的妙用。

—— 本文内容借鉴《Go语音精进之路》一书。
Go语言精进之路

一、nil channel读写阻塞

对于没有初始化的 channel (nil channel) 进行读写操作会发生阻塞,比如:

package main

func main() {
	var c chan int
	c <- 1
}

或者

package main

func main() {
	var c chan int
	<-c
}

无论是上哪段代码,运行都会得到错误的结果, main goroutine 被阻塞在 channel 上,导致 Go 运行时认为出现 deadlock状态并抛出 panic。

PS D:\workspaceGo\src\channel> go run .\nilChannel.go
fatal error: all goroutines are asleep - deadlock!

goroutine 1 [chan send (nil chan)]:
main.main()
        D:/workspaceGo/src/channel/nilChannel.go:5 +0x25
exit status 2

二、nil channel 妙用

2.1 一个普通示例

一般我们在程序中习惯用 channel、goroutine 和 select 来搭配运行,例如:

下面的示例代码中,声明了 c1 c2 两个 channel,并且启动两个 goroutine 向 c1 c2 中延时后添加值,在 for 循环中用 select case 获取到 c1 c2 中的值。

package main

import (
	"fmt"
	"time"
)

func main() {
	// 声明 c1 c2 两个通道,并且用 make 函数实例化
	c1, c2 := make(chan int), make(chan int)

	go func() {
		time.Sleep(time.Second * 5)
		c1 <- 5
		close(c1)
	}()

	go func() {
		time.Sleep(time.Second * 7)
		c2 <- 7
		close(c2)
	}()

	var ok1, ok2 bool
	for {
		select {
		case x := <-c1:
			ok1 = true
			fmt.Println(x)
		case x := <-c2:
			ok2 = true
			fmt.Println(x)
		}
		if ok1 && ok2 {
			break
		}
	}
	fmt.Println("program end!")
}

在上面的示例中,我们期望程序在接受完 c1 和 c2 两个 channel 上的数据后就退出,但实际运行结果如下:

PS D:\workspaceGo\src\channel> go run .\nilChannel.go
5
0
0
0
...
7
program end!

期望是程序在输出 5 和 7 之后退出,但实际结果中,输出完 5 之后,程序多输出了几个 0 之后才退出。

2.2 示例运行分析

  1. 程序开始运行,前 5s,select 一直处于阻塞状态。
  2. 第 5s,c1 返回一个 5 后被关闭了,select 语句的 case x := <- c1 分支被执行,程序输出 5,则 for 循环开始新的 select 执行。
  3. c1 已经被关闭,由于从一个已经的关闭的 channel 接受数据将永远不会被阻塞,所以新一轮 select 又将 case x := <- c1 被执行,由于 c1 是关闭状态的,从这个 channel 获取到对于类型的零值,即 0,于是程序输出了 0。所以在 for 循环里面,则一直输出 0 值。
  4. 2s 后,c2 被写入一个数值 7,此时在某一轮循环中,select 则选出 case x: <- c2 并执行。程序输出 7 之后满足条件退出循环,程序终止。

2.3 如何妙用 nil channel

nil channel并非一无是处,nil channel 只要用对了地方,用对了时候,就可以达到事半功倍的效果,将上面的实例程序做了改进,示例代码如下:

package main

import (
	"fmt"
	"time"
)

func main() {
	// 声明 c1 c2 两个通道,并且用 make 函数实例化
	c1, c2 := make(chan int), make(chan int)

	go func() {
		time.Sleep(time.Second * 5)
		c1 <- 5
		close(c1)
	}()

	go func() {
		time.Sleep(time.Second * 7)
		c2 <- 7
		close(c2)
	}()

	for {
		select {
		case x, ok := <-c1:
			//判断是否获取成功,不成功则把 c1 置为 nil
			if !ok {
				c1 = nil
			} else {
				fmt.Println(x)
			}
		case x, ok := <-c2:
			//判断是否获取成功,不成功则把 c2 置为 nil
			if !ok {
				c2 = nil
			} else {
				fmt.Println(x)
			}
		}
		if c1 == nil && c2 == nil {
			break
		}
	}
	fmt.Println("program end!")
}

程序的关键变化在判断 c1 或者 c2被关闭后,显示地把 c1 c2 两个 channel 置为了 nil。
有什么效果呢? 因为 对一个 nil channel 执行获取操作,该操作会被阻塞 ,因此已经被置为 nil 的 c1 c2 再也不会被 select 选中执行了。
运行结果:

PS D:\workspaceGo\src\channel> go run .\nilChannel.go 
5
7
program end!

三、总结

此篇主要在特殊场景下,用了 nil channel 来巧妙的终结了 for select 程序,避免了运行结果的错误,因为 对一个 nil channel 执行获取操作,该操作会被阻塞,但对 一个一个关闭了的 channel 执行获取操作,则会得到 0值

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

STM32F4VGT6-DISCOVERY:uart1驱动

对于这款板子&#xff0c;官方并没有提供串口例程&#xff0c;只能自行添加。 一、PA9/PA10复用成串口1功能不可用 驱动测试代码如下&#xff1a; main.c: #include "main.h" #include <stdio.h>void usart1_init(void) {GPIO_InitTypeDef GPIO_InitStruct…

前端 : 用html ,css,js写一个你画我猜的游戏

1.HTML&#xff1a; <body><div id "content"><div id "box1">计时器</div><div id"box"><div id "top"><div id "box-top-left">第几题:</div><div id "box…

linux-磁盘应用

目录 一、磁盘内容简述 1、一些基本概念 2、分区简述 3、常见文件系统 4、linux硬盘文件 二、对linux系统进行分区 1、用fdisk进行分区 2、用parted进行分区 一、磁盘内容简述 1、一些基本概念 - 扇区大小&#xff1a;512Btyes&#xff0c;0.5KB - 磁盘最小存储单位&…

小黑子—spring:第一章 Bean基础

spring入门1.0 一 小黑子对spring基础进行概述1.1 spring导论1.2 传统Javaweb开发困惑及解决方法1.3 三大的思想提出1.3.1 IOC入门案例1.3.2 DI入门案例 1.4 框架概念1.5 初识spring1.5.1 Spring Framework 1.6 BeanFactory快速入门1.7 ApplicationContext快速入门1.8 BeanFact…

python之计算平面点集的的面积

在当今数据驱动的世界中&#xff0c;计算平面点集的最小外接轮廓面积被广泛应用于各种实际场景中。它是一项重要而魅力十足的任务&#xff0c;旨在找到一个最小的矩形或多边形区域&#xff0c;能够完全包围给定的离散点集。这个看似简单的问题背后隐藏着许多挑战&#xff0c;需…

HTML5+CSS3+Vue小实例:路飞出海的动画特效

实例:路飞出海的动画特效 技术栈:HTML+CSS+Vue 效果: 源码: 【HTML】 <!DOCTYPE html> <html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"><meta name="viewport" content=&…

windows下OOM排查

如下有一段代码 package com.lm.demo.arthas.controller;import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController;import java.util.A…

车载音频项目

加我微信hezkz17进数字音频系统研究开发交流答疑群(课题组) ー 1&#xff0e;负责此项目的音频链路的设计及其实现 在ADSP21375上实现音频链路的处理。如噪声门&#xff0c;压限器&#xff0c;高低通&#xff0c;PEQ、各种效果等。 2&#xff0e;负责DSP与MCU端SPI协议实现。M…

Python文件——使用Python读取txt文件

作者&#xff1a;Insist-- 个人主页&#xff1a;insist--个人主页 本文专栏&#xff1a;Python专栏 专栏介绍&#xff1a;本专栏为免费专栏&#xff0c;并且会持续更新python基础知识&#xff0c;欢迎各位订阅关注. 目录 一、文件的编码 1. 什么是编码 2. 常见的编码 二、P…

【纯离线】Ubuntu离线安装ntp时间同步服务

Ubuntu离线安装ntp服务 准备阶段&#xff1a;下载安装包 apt-get download ntp apt-get download ntpdate 一、服务端( 192.166.6.xx) 1、环境准备 先判断是否已安装 systemd-timesyncd systemctl is-active systemd-timesyncd 如果返回结果是 active&#xff0c;则表示…

文件夹批量改名:如何在文件夹名左边添加递增的自动编号

在文件管理的过程中&#xff0c;我们有时需要对文件夹进行重命名&#xff0c;使其更具区分度和可读性。为了实现这一目标&#xff0c;我们可以采用在文件夹名左边添加递增的自动编号的方法。本文将介绍云炫文件管理器如何进行文件夹批量改名&#xff0c;以在文件夹名左边添加递…

mathtype7.4破解永久激活码

MathType(数学公式编辑器)是由Design Science公司研发的一款专业的数学公式编辑工具。MathType功能非常强大&#xff0c;尤其适用于专门研究数学领域的人群使用。使用MathType让你在输入数学公式的时候能够更加的得心应手&#xff0c;各种复杂的运算符号也不在话下。 MathType最…

Vue3.0插槽

用法&#xff1a; 父组件App.vue <template><div><!--将html代码插入到子组件中带默认名称的插槽中--><AChild><!--这段html会插入到AChild组件中<slot></slot>插槽中--><!-- 注意&#xff1a;写在父组件中的html代码只能在父组…

百度网盘使用指南

文章目录 备份篇手机文件备份电脑文件备份 查找篇移动端PC端 文件操作文件解压文件扫描PDF工具图片工具音频操作 备份篇 手机文件备份 在百度网盘APP种点击 我的–设置–自动备份设置 里边有相册备份, 文档备份, 微信文件备份, 手机通讯录, 短信, 通话备份等功能 电脑文件备…

目标检测类项目数据集汇总

一、玩手机数据集及检测 玩手机数据集下载地址分享: https://download.csdn.net/download/qq_34717531/19870205 二、狗的数据集及检测 狗目标检测数据集下载地址分享:https://download.csdn.net/download/qq_34717531/20813390 三、猫数据集及检测 猫数据集下载地址分享: ht…

回归算法|长短期记忆网络LSTM及其优化实现

本期文章将介绍LSTM的原理及其优化实现 序列数据有一个特点&#xff0c;即“没有曾经的过去则不存在当前的现状”&#xff0c;这类数据以时间为纽带&#xff0c;将无数个历史事件串联&#xff0c;构成了当前状态&#xff0c;这种时间构筑起来的事件前后依赖关系称其为时间依赖&…

c++设计模式三:工厂模式

本文通过一个例子简单介绍简单工厂模式、工厂模式和抽象工厂模式。 1.简单工厂&#xff08;静态&#xff09; 假如我想换个手机&#xff0c;换什么手机呢&#xff1f;可以考虑苹果或者华为手机&#xff0c;那我们用简单工厂模式来实现这个功能&#xff1a; 我们关注的产品是手…

基于群居蜘蛛算法的无人机航迹规划

基于群居蜘蛛算法的无人机航迹规划 文章目录 基于群居蜘蛛算法的无人机航迹规划1.群居蜘蛛搜索算法2.无人机飞行环境建模3.无人机航迹规划建模4.实验结果4.1地图创建4.2 航迹规划 5.参考文献6.Matlab代码 摘要&#xff1a;本文主要介绍利用群居蜘蛛算法来优化无人机航迹规划。 …

【数据结构初阶】顺序表和链表(1)

顺序表和链表&#xff08;1&#xff09; 1.线性表2.顺序表2.1概念以及结构2.1.1静态顺序表2.1.2动态顺序表3.顺序表的实现3.1初始化内容3.2初始化函数3.3销毁函数3.4打印函数3.5扩容函数3.6尾插3.6尾删函数3.7头插函数3.8头删函数3.9查找函数3.10插入函数3.11删除函数3.12修改函…

拿到 phpMyAdmin 如何获取权限

文章目录 拿到 phpMyAdmin 如何获取权限1. outfile 写一句话木马2. general_log_file 写一句话木马 拿到 phpMyAdmin 如何获取权限 1. outfile 写一句话木马 尝试使用SQL注入写文件的方式&#xff0c;执行 outfile 语句写入一句话木马。 select "<?php eval($_REQU…