GOLANG进阶 之 接口(interface) 与 管道(channel)

news2024/12/23 8:55:16

        好久没有跟新过文章了,小编最近有点忙,写文章的频率下降了许多,但是还是会持续跟新的,希望关注的同学仔细学习。

        首先讲一下接口具体是个啥?小白可以结合官方定义和小编自己的理解共同学习下

        官方解释:接口(interface)定义了一个对象的行为规范,只定义规范不实现,由具体的对象来实现规范的细节。

        自己理解:面向过程的开发 变成  面向对象编程;相当于生成了一个类,需要这个类的功能直接调用就好,不用再重新定义

       

怎么做?简单来说就是:接口调用(结构体作为函数入参):第二步和第三步可以合并


第一步:结构体
type User struct {
         属性名  值

        ..................................
}

第二步:定义方法
func (z *User ) Pay(amount int64) {
  fmt.Printf("测试保留两位小数:%.2f。\n", float64(amount/100))
}

第三步:定义方法
func Checkout(obj *User ) {
    obj.Pay(100)
}

第四步:主方法调用
func main() {
    Checkout(&User {})
}

输出值:测试保留两位小数:100.00

最基础的就是这样的,其余的以后会结合项目讲解,先打好基础,后面学习才会更轻松

        管道(channel)又是个啥?等小编一一解释(这里有必要学习一下调度机制GPM,这个后面小编会单独讲解)

        官方解释:可以看作是一个通信的桥梁,用于连接不同操作的 goroutine。当多个 goroutine 同时访问同一资源时,我们就需要使用管道来协调它们的访问,锁具有一定的局限性,因为当我们使用锁时,它只允许一个 goroutine 访问资源,而通常情况下我们需要更高效的方法协调 goroutine 的并发操作,这时就需要用到管道。并且管道是一种并发安全的数据结构

        自己理解:一个goroutine的队列,保证goroutine的有序执行

        了解知识:并发模型

            线程&锁模型
            Actor模型
            CSP模型
            Fork&Join模型

下面以代码的形式来理解下:

代码片段1:

func hello() {
    fmt.Println("hello")
}

func main() {
    hello()
    fmt.Println("你好")
}

输出:
hello
你好

代码片段2:

func hello() {
    fmt.Println("hello")
}

func main() {
    go hello() // 启动另外一个goroutine去执行hello函数
    fmt.Println("你好")
}

输出:
你好

为什么会出现这样的情况?

        go关键字:启动一个 goroutine 去执行 hello 这个函数,这样可以模拟并发

为什么只执行了最后的输出?
        其实在 Go 程序启动时,Go 程序就会为 main 函数创建一个默认的 goroutine

        在上面的代码中我们在 main 函数中使用 go 关键字创建了另外一个 goroutine 去执行 hello 函数,而此时 main goroutine 还在继续往下执行,我们的程序中此时存在两个并发执行的 goroutine。
        当 main 函数结束时整个程序也就结束了,同时 main goroutine 也结束了,所有由 main goroutine 创建的 goroutine 也会一同退出。也就是说我们的 main 函数退出太快,另外一个 goroutine 中的函数还未执行完程序就退出了,导致未打印出“hello”。

图形解释:(解释代码片段1)

所以要有输出两个值的效果就需要sync 包中的WaitGroup的功能

var wg sync.WaitGroup

func hello() {
    fmt.Println("hello")
    wg.Done() // 当前goroutine完成
}

func main() {
    wg.Add(1) // 登记1个goroutine
    go hello()
    fmt.Println("你好")
    wg.Wait() // 阻塞等待的goroutine完成
}

输出:
    hello
    你好

图形解释:

        虽然可以使用共享内存进行数据交换,但是共享内存在不同的 goroutine 中容易发生竞态问题,为了保证数据交换的正确性,很多并发模型中必须使用互斥量对内存进行加锁,这种做法势必造成性能问题

        Go语言采用的并发模型是CSP(Communicating Sequential Processes),提倡通过通信共享内存而不是通过共享内存而实现通信。

        重点:如果说 goroutine 是Go程序并发的执行体,channel就是它们之间的连接。channel是可以让一个 goroutine 发送特定值到另一个 goroutine 的通信机制。通道像一个传送带或者队列,总是遵循先入先出(First In First Out)的规则,保证收发数据的顺序。每一个通道都是一个具体类型的导管,也就是声明channel的时候需要为其指定元素类型。

初始化管道:       

var ch1 chan int   // 声明一个传递整型的通道
var ch2 chan bool  // 声明一个传递布尔型的通道
var ch3 chan []int // 声明一个传递int切片的通道
ch4 := make(chan int)
ch5 := make(chan bool, 1)  // 声明一个缓冲区大小为1的通道

管道的使用:将一个值发送到通道中
ch1 <- 10 

管道的使用:从一个通道中接收值
x := <- ch1

管道的使用:关闭管道
close(ch1 )
基础使用方法已经讲解完成,后面延伸讲一下:
    


无缓冲管道:

func main() {
    ch := make(chan int)
    ch <- 10
    fmt.Println("发送成功")
}
报错信息:

fatal error: all goroutines are asleep - deadlock! //所有goroutine都处于休眠状态-死锁!
goroutine 1 [chan send]:

        首先无缓冲通道ch上的发送操作会阻塞,直到另一个 goroutine 在该通道上执行接收操作,这时数字10才能发送成功,两个 goroutine 将继续执行。
        相反,如果接收操作先执行,接收方所在的 goroutine 将阻塞,直到 main goroutine 中向该通道发送数字10。
        使用无缓冲通道进行通信将导致发送和接收的 goroutine 同步化。因此,无缓冲通道也被称为同步通道
    

解决办法1:为什么这个就可以?

        首先无缓冲通道ch上的发送操作会阻塞,直到另一个 goroutine 在该通道上执行接收操作,这时数字10才能发送成功,两个 goroutine 将继续执行。

        相反,如果接收操作先执行,接收方所在的 goroutine 将阻塞直到 main goroutine 中向该通道发送数字10
        使用无缓冲通道进行通信将导致发送和接收的 goroutine 同步化。因此,无缓冲通道也被称为
同步通道


func recv(c chan int) {
    ret := <-c
    fmt.Println("接收成功", ret)
}

func main() {
    ch := make(chan int)
    go recv(ch) 
    ch <- 10
    fmt.Println("发送成功")
}
    
    
解决办法2:缓冲管道

func main() {
    ch := make(chan int, 1) // 创建一个容量为1的有缓冲区通道
    ch <- 10
    fmt.Println("发送成功")
}
    

现阶段理解到这个程度就可以了,后续深入的小编再讲,结合项目实战讲

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

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

相关文章

MATLAB程序初始化OpenFOAM颗粒位置

问题引入 在OpenFOAM的颗粒两相流求解器中&#xff0c;我们可以采用manualInjection的方式进行自定义颗粒的初始位置&#xff0c;这个命令十分方便&#xff0c;在CFDEM中也有类似的命令&#xff0c;不过CFDEM中的命令更加强大&#xff0c;我们不仅可以定义颗粒的初始位置&…

Dockerfile 简单实战

将flask项目打包成镜像 1. 准备flask文件 创建 app.py 文件&#xff0c;内容如下 from flask import Flask app Flask(__name__)app.route(/) def hello_world():return Hello Worldif __name__ __main__:app.run(host0.0.0.0, port8000, debugTrue) 并开启外网访问&#xf…

C# 使用FFmpeg.Autogen对byte[]进行编解码

C# 使用FFmpeg.Autogen对byte[]进行编解码&#xff0c;参考&#xff1a;https://github.com/vanjoge/CSharpVideoDemo 入口调用类&#xff1a; using System; using System.IO; using System.Drawing; using System.Runtime.InteropServices; using FFmpeg.AutoGen;namespace F…

云原生是什么

目录 1. 云原生是什么1.1. 微服务1.2. DevOps1.3. 持续交付1.4. 容器化 2. 什么是云原生2.1. 云原生的诞生2.2. 云原生基金会2.3. 主要区别: 云原生与传统企业应用 1. 云原生是什么 云原生是面向"云"而设计的应用, 因此技术部分依赖于传统云计算的 3 层概念, 基础设…

二叉树题目:根据二叉树创建字符串

文章目录 题目标题和出处难度题目描述要求示例数据范围 解法一思路和算法代码复杂度分析 解法二思路和算法代码复杂度分析 题目 标题和出处 标题&#xff1a;根据二叉树创建字符串 出处&#xff1a;606. 根据二叉树创建字符串 难度 3 级 题目描述 要求 给你二叉树的根结…

Git 代码分支规范

目的 俗话说&#xff1a;没有规矩&#xff0c;不成方圆。遵循一个好的规章制度能让你的工作事半功倍。同时也可以展现出你做事的认真的态度以及你的专业性&#xff0c;不会显得杂乱无章&#xff0c;管理困难。Git分支规范也是一样。当遵循了某种约定的Git分支&#xff0c;在代…

欧拉公式的证明-泰勒展开法

欧拉公式 欧拉公式在理工科有着广泛的应用和影响。 特别地&#xff0c;当时&#xff0c;&#xff0c;巧妙地将自然对数底数 &#xff0c;圆周率 &#xff0c;虚数单位 &#xff0c; 写进一个公式。 证明 由泰勒公式&#xff1a; 即 提取奇偶次项&#xff1a; 即 补充&#xf…

Michael.W基于Foundry精读Openzeppelin第23期——ERC165Checker.sol

Michael.W基于Foundry精读Openzeppelin第23期——ERC165Checker.sol 0. 版本0.1 ERC165Checker.sol 1. 目标合约2. 代码精读2.1 supportsERC165InterfaceUnchecked(address account, bytes4 interfaceId)2.2 supportsERC165(address account)2.3 supportsInterface(address acc…

Docker一键部署项目,无需登录XShell

文章目录 一键部署项目Docker手动部署SpringBoot项目编写docker部署的脚本文件script.sh 脚本内容 特别注意&#xff01;编写dockerfiledockerfile 文件内容 上传后端服务的jar包到服务器中执行 script 脚本部署后端服务 自动部署SpringBoot项目引入jsch依赖编写jsch工具类执行…

C# byte[]与Bitmap互转

首先先观察一下本地bmp图像结构(参考&#xff1a; https://blog.csdn.net/qq_37872392/article/details/124710600)&#xff1a; 可以看到bmp图像结构除了纯图像像素点位信息&#xff0c;还有一块未用空间(OffSet)。 所以如果需要得到图像所有数据进行转换&#xff0c;则可以使…

MySQL_SQL性能分析

SQL执行频次 语法&#xff1a; SHOW GLOBAL STATUS LIKE COM_类型; COM_insert; 插入次数 com_delete; 删除次数 com_update; 更新次数 com_select; 查询次数 com_______; 注意&#xff1a;通过语法&#xff0c;可以查询到数据库的实际状态&#xff0c;就可以知道数据库是以增删…

macos搭建appium-iOS自动化测试环境

目录 准备工作 安装必需的软件 安装appium 安装XCode 下载WDA工程 配置WDA工程 搭建appiumwda自动化环境 第一步&#xff1a;启动通过xcodebuild命令启动wda服务 分享一下如何在mac电脑上搭建一个完整的appium自动化测试环境 准备工作 前期需要准备的设备和账号&…

【独立版】新零售社区团购电商系统生鲜水果商城兴盛优选十荟团源码

【独立版】新零售社区团购电商系统生鲜水果商城兴盛优选十荟团源码

DNSlog注入(利用DNSlog平台将SQL盲注变成回显注入)

前言什么是UNC什么是DNSlog注入DNSlog注入的条件防止DNSlog注入的几个措施 sqli-labs试验 前言 前几天面试的时候&#xff0c;面试官问我知不知道OOB&#xff08;带外数据&#xff09;。 当时我蒙了&#xff0c;确实没听说过这个东西&#xff0c;然后面试官告诉我原来dnslog注入…

机器人CPP编程基础-04输入Input

机器人CPP编程基础-03变量类型Variables Types ……AI…… C #include<iostream> // 引入iostream库&#xff0c;这个库包含了对输入/输出进行操作所需的函数和对象 using namespace std; // 使用命名空间std&#xff0c;这样我们就可以直接使用std中的名字&#xff0c…

设定嵌入式linux系统的用户名和密码

遇到一个问题&#xff0c;板子上电后&#xff0c;串口可以正常输入命令行&#xff0c;而且不需要密码&#xff0c;用户名就是root &#xff0c;因为没有设置密码&#xff0c;但是SSH登录时用户名输入root&#xff0c;密码直接敲回车键也就是不输入密码竟然是错误的&#xff0c;…

一文带你入门Nacos:从安装到实例分析

目录 一、安装和配置 1.1 下载安装包 1.2 解压 1.3 端口配置 1.4 启动 1.5 访问 二、服务注册到nacos 2.1 引入依赖 2.2 配置nacos地址 2.3 重启 三、服务分级存储模型 3.1 给user-service配置集群 3.2 同集群优先的负载均衡 1&#xff09;给order-service配…

勘探开发人工智能技术:机器学习(3)

0 提纲 4.1 logistic回归 4.2 支持向量机(SVM) 4.3 PCA 1 logistic回归 用超平面分割正负样本, 考虑所有样本导致的损失. 1.1 线性分类器 logistic 回归是使用超平面将空间分开, 一边是正样本, 另一边是负样本. 因此, 它是一个线性分类器. 如图所示, 若干样本由两个特征描…

竞赛项目 深度学习疲劳驾驶检测 opencv python

文章目录 0 前言1 课题背景2 实现目标3 当前市面上疲劳驾驶检测的方法4 相关数据集5 基于头部姿态的驾驶疲劳检测5.1 如何确定疲劳状态5.2 算法步骤5.3 打瞌睡判断 6 基于CNN与SVM的疲劳检测方法6.1 网络结构6.2 疲劳图像分类训练6.3 训练结果 7 最后 0 前言 &#x1f525; 优…

Python爬虫 爬取图片

在我们日常上网浏览网页的时候&#xff0c;经常会看到一些好看的图片&#xff0c;我们就希望把这些图片保存下载&#xff0c;或者用户用来做桌面壁纸&#xff0c;或者用来做设计的素材。 我们最常规的做法就是通过鼠标右键&#xff0c;选择另存为。但有些图片鼠标右键的时候并没…