【Go语言】

type关键字的用法

  • 定义结构体
  • 定义接口
  • 定义类型别名
  • 类型定义
  • 类型判断

别名实际上是为了更好地理解代码/

这里要分点进行记录  使用传值的例子,当两个类型不一样需要进行类型转换

type Myint int  // 自定义类型,基于已有的类型自定义一个类型type Myint = int
// 在编译的时候,类型别名会被直接替换为int

上面两种方式定义的int是不同的,因为使用等于号是类型别名,不使用等于号的是重新定义了一个类型。

type Myint int 

如果我们即想使用int类型,又想让这种类型的变量有自己的方法,我们可以使用type自定义一种类型,这其实也算是一种结构体,我们可以为这个结构体创建一个方法,我们就可以进行调用这个方法。

type Myint=int

这个语句就是单独地给int变量起一个别名,其他用法和int一模一样。

结构体的定义和初始化

在go语言中没有面向对象的概念,但是可以使用面向对象的思想

type Person struct {name stringage intaddress stringheight float32
}// 在进行初始化的时候,我们有两种方式:
p1 := Peason{"xiaomin", 14, "佳", 1.8}p2 := Person{name:"xiaomin",age:14,
}

匿名结构体  匿名函数

只能使用一次。

address := struct {province stringcity stringaddress string
}{"北京市","通州区","xxx",
}

结构体嵌套

有两种嵌套方式

type Person struct {name stringage int
}type student struct {p Personscore int
}type student struct {Personscore int
}

匿名定义中有一些问题,如果匿名结构体中有name,但是在外边的结构体中也有name,那么我们会发现外部的结构体的name的优先级大于匿名结构体中的优先级。

结构体定义方法

接收器有两种形态,一种是值传递,一种是指针传递,当我们需要进行修改结构体中的数值或者传递的结构体的容量很大时,我们需要进行指针传递。

如果接收器是指针类型,但是我们是值类型,我们可以进行调用函数的,同理,另一种情况也是可以的。

指针:

go语言中限制了指针的运算,在C语言中,我们可以使用指针进行加减运算, 

指针的初始化

指针是需要进行初始化的

在初始化的时候,有两个关键字:make ,new  

map channel  slice  初始化推荐使用make方法

指针初始化推荐使用new函数,指针需要进行初始化,否则会出现nil pointer

map必须初始化,否则会出现panic 

nil在go中的细节

不同类型的数据的零值不一样,

bool false

numbers 0

string ""

pointer nil

slice nil

map nil

channel  interface function nil

struct 的默认值不是nil,默认值是具体字段的默认值

slice的底层介绍???????等会看一看

map也分为两种 nil map 和 empty map 在大部分场景下,nil map 和 empty map一样,但是当我们给nil map 进行赋值的时候,会进行panic

接口

什么是鸭子类型?

方法。动词,具备某些方法,go语言中强调的是动作

鸭子类型强调的是外部行为,而不是内部的结构

如何定义一个接口

type Duck interface{// 方法的申请Gaga()Walk()Swiming()
}type pskDuck struct {legs int
}func (pd *pskDuck) Gaga() {fmt.Println("this is walking...")
}func main() {var d Duck = &pskDuck{}d.Gaga()
}

多接口的实现  不是很懂

如果有两个接口中的函数出现重复,说明我们可以将函数进行复用,我们可以实现接口进行解耦。

type MyWriter interface{Write(string) error
}type MyCloser interface{Close() error
}type WriteClose struct {MyWriter  // interface 也是一种类型 
}func (wc *WriteClose) Writer(s string) error {fmt.Println("this is writer)return nil
}func (wc *WriteClose) Close() error {fmt.Println("this is closer")return nil
}

在go语言中,多接口的实现是通过组合接口来实现的,go不支持多重继承,但是可以让一个类型实现多个接口,具体来说,只要一个类型实现了某些方法,他就自动实现了相应的接口。通过这种方式,go语言允许一个类型轻松实现多个接口,促进了灵活的设计和代码复用。

通过interface解决动态类型传参

我们在实现一些

使用断言转换类型  

但是还是没有解决问题

使用switch语句,断言处理分支语句可以用到

a.(type) 可以判断是什么类型 

但是更通用的写法是泛型。

string.Split

接口嵌套

接口遇到了slice的常见错误

单元测试

Go的并发编程

go的并发编程初体验

       在php,java,python等语言中,都是有多线程编程,多进程编程,多线程和多进程存在的问题主要是耗费内存。在进行进程切换和线程切换的时候,CPU会消耗一些资源。因此在go语言中,我们推出了协程(用户级线程,绿程,轻量级线程)。

       在协程中,内存占用小(2k),切换快,在go语言的协程中,go余元诞生之后就只有协程可以使用——goroutine,非常方便。我们可以在程序中使用协程创建出10万以上的协程,但是线程在程序中只能创建出100多个。下面,我们来看一看协程的代码:

func asyncPrint() {fmt.Println("加油旭杏")
}// 主协程
func main() {go asyncPrint()
}

       上述代码是单个协程的调用,如果我们想使用多协程进行调用,我们可以使用循环来创建出多个协程。在创建协程的时候,我们有两种方式可以进行创建:一种是直接调用函数,一种是匿名函数。代码如下:

func Print() {fmt.Println("加油旭杏")
}// 匿名结构体
func main() {go func() {for {fmt.Println("加油旭杏")}}time.Sleep(10 * time.Second)
}

       这里为了使程序不退出,我们在主协程中添加了睡眠时间,但是这种方式是非常挫的,在下面我们将会学习另一种等待协程的方法。

有一个问题:在C++中,如果有多个线程去执行一个加法运算,再执行一个减法运算会出现错误的,我们需要使用锁来进行让加减操作变成原子操作。如果操作简单,我们也可以使用原子变量来进行操作。

我们来看一看下面的代码:

for i := 0; i < 100; i++ {go func() {fmt.Println(i)}()
}

       第一个原因是我们使用匿名函数,也就是闭包,只能使用一次,第二个原因是我们使用了for循环,在for循环中,每一个变量会被重新使用,所以我们在调用加操作之前的变量和调用加操作之后的变量不是同一个变量,因为这个不是原子操作,也会CPU的调度有关。

go的gmp调度原理

这个gmp调度是分层的,我们先来看一看两层结构:一个是协程,一个是线程;在Go语言中,我们会将协程放入到线程去执行,

一张图   g  gorounte  m     p

我们在协程调度的时候,我们需要将

通过waitgroup来等待协程

       子gorounte如何通知到主的gorount自己结束了,主的gorounte如何知道子的gorounte已经结束了??

       之前我们使用的例子是通过设置时间长度,但是我们不知道具体需要多少时间,所以这个代码是比较挫的,现在我们来认识一下WaitGroup。我们可以通过WaitGroup来将协程加入,如果我们事先知道协程的总数,可以直接在循环之前进行添加;如果我们不知道协程的总数,我们可以在循环的时候进行添加,每循环一次就添加一次,在添加之后我们需要将其进行done,类似于锁的机制一样,否则会造成死锁。因此,我们需要知道WaitGroup的add和done是需要配套使用的。最后我们需要进行wait方式进行等待。代码如下:

var wg sync.WaitGroup// 我们需要监控多少个gotountine执行结束
wg.Add(100)
for i := 0; i < 100; i++ {go func(i int) {fmt.Println(i)wg.Done(); // Done和Add要成对出现}()
}// 等待
wg.Wait()

       waitgroup主要用于gorountine的执行等待,add方法主要和done方法配套,最后要使用Wait方法进行等待,否则会出现死锁。

通过mutex和atomic完成全局变量的原子操作

       当我们需要进行共享资源的访问时,我们需要进行锁来对于资源竞争的保证。下面,我们来看一看代码:

func add() {defer wg.Done()for i := 0; i < 10000; i++ {lock.Lock()total += 1  // 这里会有竞争lock.Unlock()}
}func sub() {defer wg.Done()for i := 0; i < 10000; i++ {lock.Lock()total -= 1  // 这里会有竞争lock.Unlock()}
}

       当我们单独执行add函数或者单独执行sub函数,结果都是正确,但是当我们两个一起执行时,结果是错的,在Linux中,我们也出现了这种情况,因为a += 1不是原子操作,当CPU进行执行这段代码时,有可能中途退出,我们需要将这段代码进行保护起来,不要让他单独执行。在go语言中的锁是一个结构体,锁可以进行复制,但是复制过后锁就失去了自身的效果。下面是将加减操作改为原子操作,代码如下:

func add() {defer wg.Done()for i := 0; i < 10000; i++ {atomic.AddInt32(&total, 1)}
}func sub() {defer wg.Done()for i := 0; i < 10000; i++ {atomic.AddInt32(&total, -1)}
}

RWMutex 读写锁

       锁本质上就将并行的代码串行化,使用lock会影响性能。即使是设计锁,那么也应该保持并行。

       我们有两组协程,其中一组负责写数据,另一组负责读数据,web系统中绝大部分场景都是读多写少。虽然有多个gorounte,但是仔细分析我们会发现读是可以进行并发,读和写应该进行串行。我们在读的时候不能写,在写的时候不能读。

       这里我们就出现了读写锁m我们在进行读写锁的时候,我们需要将写操作的时候添加写锁:

  • 写锁会防止别的写锁获取和读锁获取,使用WaitGroup
  • 读锁不会阻止别人的锁,写锁会阻止别人的锁。
for i := 0; i < 5; ++i {go func() {defer wg.Done()for {rwlock.RLock()  // 加读锁,读锁不会阻止别人的读// TODOrwlock.RUnlock()}}
}

使用channel进行gorounte之间的通信

channel通道

在Go语言中不要通过共享内存进行通信,而是要通过通信来实现内存共享

       在C++中如何实现进程之间的通信,就是使用一个全局变量(两个线程都可以看到的)来实现通信,在其他语言中的多线程编程的时候,两个gorountine之间通信最常用的方式是channel,再加上语法糖让channel更加简单。

        channel初始化值为0,放值进去会阻塞。

func main() {var msg chan string // 定义一个通道msg = make(chan string, 1)msg <- "加油"data := <- msgfmt.Println(data)
}

       静态语言中使用channel,我们需要指明通道中的类型是什么??注意箭头的指向,如果定义的通道在箭头的左边,则是将数据放入到通道中,如果定义的通道在箭头的右边,表示从通道中取数据。

channel有缓冲和无缓冲

var msg chan stringmsg = make(chan string, 0)  // channel的初始值如果为0的话,你放值进去会进行阻塞go func(msg chan string) {// go语言中有一种 happen-before机制,可以保证线程在channel中有值才会进行data := <- msgfmt.Println(data)
}(msg)msg <- "加油旭杏" // 放值到channel中
time.Sleep(time.Second * 10)

无缓冲区channel适用于 通知,B要第一时间知道A是否完成

有缓冲区channel适用于消费者和生产者之间的通信 

go中channel的应用场景

  • 消息传递,消息过滤
  • 信号广播
  • 事件订阅和广播
  • 任务分发
  • 结果汇总
  • 并发控制
  • 同步和异步

       在默认情况下,channel是双向的,但是我们经常使用channel作为参数进行传递,希望对方是单向使用的。 双向channel可以赋值给单向channel,但是单向channel不能赋值给双向channel,代码如下:

var ch1 chan int
var ch2 chan<- float64 // 单向channel,只能写入数据
var ch3 <-chan int // 只能读取数据

我们需要进行通信,就需要进行

通过channel实现交叉打印

我们需要使用channel进行打印,我们使用channel去进行通知,好像信号量,就是我们先打印数字然后唤醒打印字符的,在去打印字符,打印完字符再去唤醒打印数字的,如此交替执行。

监控gorounte的执行

       在go语言中,select类似于switch case 语句,但是select的功能和我们操作Linux里面提供的io的select,poll,epoll不一样,select主要作用于多个channel。

       现在有一个需求,我们现在有两个gorounte正在执行,但是我的主的gorounte中,当某一个执行完成之后,这个时候我们会立马知道

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

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

相关文章

用kali入侵 DarkHole_2测试

进入kali系统调出root交互式界面 netdiscover -r 000.000.000.000/24 -------局域网探测IP工具 nmap 设备端口扫描 发现两个攻击点一个是80端口的Http 一个是22端口的ssh 发现有许多GIT文件 可能会出现git源码泄露 使用githack URL 命令还原git源文件 打开面板控制命令行 输入…

2.插入排序(斗地主起牌)

一、思想 扑克牌起牌 代码&#xff1a; 二、时间复杂度&#xff1a; 最好情况&#xff08;已经排序好的&#xff09;&#xff1a;T O(N) 最坏情况&#xff08;完全逆序&#xff09;&#xff1a;T O(N^2) 三、优劣&#xff1a; 严格的大小比较之后才进行错位插入&#x…

unity中GameObject介绍

在 Unity 中&#xff0c;Cube和Sphere等基本几何体是 Unity 引擎的内置预制体&#xff08;Prefabs&#xff09;&#xff0c;它们属于 Unity 中的GameObject 系统&#xff0c;可以在 Unity 的 Hierarchy 视图或 Scene 视图中右键点击&#xff0c;然后在弹出的菜单中选择 3D Obje…

JVM参数选项类型

我的后端学习大纲 JVM学习大纲 1、类型1&#xff1a;标准参数选项&#xff1a; 1.1.特点&#xff1a; 1.比较稳定&#xff0c;后续基本不会发生变化2.以“-”开头 1.2.各种选项&#xff1a; 运行java或者java -help可以看到所有的标准选项 1.3.补充内容&#xff1a; -se…

Halcon 多相机统一坐标系(标定)

多相机统一坐标系是指将多个不同位置的相机的图像采集到同一个坐标系下进行处理和分析的方法。 在计算机视觉和机器视觉领域中&#xff0c;多相机统一坐标系被广泛应用于三维重建、立体视觉、目标跟踪等任务中。 以gen_binocular_rectification_map&#xff08;生成描述图像映…

Android View的事件分发机制

前言 本文由于介绍本人关于View的事件分发机制的学习&#xff0c;如有不恰当的描述欢迎指出。 View基础 什么是View ​ View是Android中所有控件的基类&#xff0c;不管是Button、TextView、LinearLayout&#xff0c;它们的共同基类都是View。也就是说&#xff0c;View是界…

K8S配置storage-class

简介 Kubernetes支持NFS存储&#xff0c;需要安装nfs-subdir-external-provisioner&#xff0c;它是一个存储资源自动调配器&#xff0c;它可将现有的NFS服务器通过持久卷声明来支持Kubernetes持久卷的动态分配。该组件是对Kubernetes NFS-Client Provisioner的扩展&#xff0…

腾讯云跨AZ部署FortigateHA备忘录

随时保存配置 config system globalset admintimeout 480set alias "FortiGate-VM64-KVM"set gui-auto-upgrade-setup-warning disableset hostname "FG-Slave"set revision-backup-on-logout enableset revision-image-auto-backup enableset timezone &…

面向对象与设计模式第一节:深入理解OOP

第三章&#xff1a;面向对象与设计模式 第一节&#xff1a;深入理解OOP 面向对象编程&#xff08;OOP&#xff09;是一种编程范式&#xff0c;它将程序结构视为由对象组成&#xff0c;促进了代码的重用性和可维护性。在这一课中&#xff0c;我们将深入分析OOP的四个基本特性&…

[JAVAEE] 多线程的案例(三) - 线程池

目录 一. 什么是线程池 二. 线程池的作用 三. java提供的线程池类 四. ThreadPoolExecutor的构造方法及参数理解 1. int corePoolSize: 核心线程数. 2. int maximumPoolSize: 最大线程数 核心线程数 非核心线程数 3. int keepAliveTime:非核心线程允许空闲的最大时间. …

DataX简介及使用

目录 一、DataX离线同步工具DataX3.0介绍 1.1、 DataX 3.0概览 1.2、特征 1.3、DataX3.0框架设计 1.4、支持的数据元 1.5、DataX3.0核心架构 1.6、DataX 3.0六大核心优势 1.6.1、可靠的数据质量监控 1.6.2、丰富的数据转换功能 1.6.3、精准的速度控制 1.6.4、强劲的…

正则表达式和通配符

文章目录 正则表达式和通配符的区别正则表达式&#xff08;Regex&#xff09;通配符&#xff08;Wildcards&#xff09;总结 正则表达式的概念正则表达式的由来为什么要使用正则表达式 正则表达式的语法组成修饰符元字符\f\b\B 在Linux中的基础正则和扩展正则基础正则(BRE)^$.*…

面试时被问到“Scaling Law”,该怎么答?

在大模型的研发中&#xff0c;通常会有下面一些需求&#xff1a; 计划训练一个 10B 的模型&#xff0c;想知道至少需要多大的数据&#xff1f; 收集到了 1T 的数据&#xff0c;想知道能训练一个多大的模型&#xff1f; 老板准备 1 个月后开发布会&#xff0c;给的资源是 100 …

Linux安装Nginx教程(rpm安装方式)

本章教程,主要介绍如何在Linux Centos7系统上,使用rpm的方式进行安装Nginx。 一、安装wget插件 如果不存在wget下载插件,需要安装一下。 yum install -y wget二 、下载rpm安装包 官方提供的rpm下载地址:https://nginx.org/packages/centos/7/x86_64/RPMS/ <

【Nginx系列】499错误

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

Postman常见问题及解决方(全)

&#x1f345; 点击文末小卡片 &#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 1、网络连接问题 如果Postman无法发送请求或接收响应&#xff0c;可以尝试以下操作&#xff1a; 检查网络连接是否正常&#xff0c;包括检查网络设置、代理设置…

软考中级嵌入式系统设计师笔记分享(二)

1.TTL 电路是电流控制器件&#xff0c;而CMOS 电路是电压控制器件。 2.TTL 电路的速度快&#xff0c;传输延迟时间短(5-10ns)&#xff0c;但是功耗大。 常见的串行总线有 SPI、II2C、USB、RS232/RS422/RS485、CAN等;高速串行总线主要有 SATA、PCIE、IEEE 1394、Rapidl0、USB 3…

1.DBeaver连接hive数据库

1.hive开启远程服务&#xff0c;linux中直接输入&#xff1a;hiveserver2 2.解压dbeaver和hive-jdbc-2.1.1.zip 3.双击打开 4.数据库&#xff0c;新建连接 5.搜索hive 6.配置参数 7.编辑驱动设置 8.添加jar包 9.测试连接 10.右击&#xff0c;新建sql编辑器 11.执行sql 12.调整字…

【每日一题】LeetCode - 整数转罗马数字

在罗马数字系统中&#xff0c;七个不同的符号代表不同的值&#xff1a; 符号值I1V5X10L50C100D500M1000 罗马数字的表示方式是从最大值开始逐次减去每个符号的值&#xff0c;通过组合这些符号构建最终的表示形式。本文将介绍一个基于贪心策略的解决方案&#xff0c;将整数转换…

unity开发之Line Renderer

Line Renderer 是一个有用的工具&#xff0c;可让您在游戏中绘制线条。 它可以用作游戏的函数或调试标记。 在这里&#xff0c;让我们创建一个程序&#xff0c;根据基本用法在 Line Renderer 上移动。 目录 如何使用 Line Renderer 和基础知识 在场景中放置 Line Renderer关键组…