Go 语言基础知识

 
什么是 Go 语言
Go是一门 并发支持 垃圾回收 编译型 系统编程语言,旨在创造一门具有在静态编译语言的 高性能 和动态语言的 高效开发 之间拥有良好平衡点的一门编程语言。
 
一些设计思想
  • 不要通过共享内存来通信,要通过通信来共享内存
 
Go 的主要特点
  • 类型安全 内存安全
  • 以非常直观极低代价的方案实现 高并发
  • 高效的垃圾回收机制
  • 快速编译(同时解 决C语言中头文件 太多的问题)
  • 为多核计算机提供性能提升的方案
  • UTF-8编码支持
Go 代码的一般格式
  • -通过 import 关键字来导入其它 非 main
  • -通过 const关键字来进行常量的定义
  • -通过在函数体外部使用 var关键字来进行全局变量的声明与赋值
  • -通过 type 关键字来进行结构(struct)或接口(interface)的声明
  • -通过 func关键字来进行函数的声明
可见行规则:Go语言中,使用 大小写 来决定该 常量、变量、类型、接口、结构或函数 是否可以被外部包所调用
 
Go 语言的数据类型
  • 布尔型:bool、1 字节l
  • 整型:int/uint、根据平台可能是 32 位或者是 64 位
  • 8 位整型:int8/uint8、1 字节
  • 字节型:byte、1 字节
  • 16 位整型:int16/uint16、2 字节
  • 浮点型:float32/float64、4/8 字节
  • 复数:complex64/complex128、8/16字节
  • 其他值类型:array、struct、string
  • 引用类型:slice、map、chan
  • 接口类型:interface
  • 函数类型:func(引用类型)
 
Golang 的 rune 类型
// int32的别名,几乎在所有方面等同于int32// 它用来区分字符值和整数值(就是用在字符串)
byte 等同于int8,常用来处理ascii字符
rune 等同于int32,常用来处理unicode或utf-8字符
 
Golang 的 string 类型
  • string 类型使用的 utf8采用变长字节存储(英文字母是单字节存储,中文是3个字节存储)
  • len 会返回 string 的字节数,string 是字节的集合,for-range 时下标可能不连续
  • string 拼接,不要循坏使用 +,可以考虑使用 strings.Join 函数

 
Golang 的 channel 类型
使用 make 创建有无缓冲区的管道 ch1 := make(channel string,5)
使用场景:消息传递、同步异步、并发控制、结果汇总
 
type hchan struct {
    qcount   uint           // 环形队列长度,即已有元素个数 ¥
    dataqsiz uint           // 环形队列容量,即可容纳元素个数 ¥
    buf      unsafe.Pointer // 环形队列地址(底层使用数组实现)¥
    elemsize uint16         // 元素大小
    closed   uint32        // 标识关闭状态(关闭时会唤醒队列里的 channel)
    elemtype *_type     // 元素类型
    sendx    uint       // 下一次写下标位置
    recvx    uint       // 下一次读下标位置
   recvq    waitq      // 读等待队列(阻塞时加到这个队列)¥
   sendq    waitq      // 写等待队列(阻塞时加到这个队列)¥
   lock     mutex          // 互斥锁 ¥
}
 
阻塞场景
  1. 向无缓冲的队列写数据或者读数据
  1. 读缓冲区无数据,写缓冲区已满
panic 场景
  • 关闭已经关闭或者为 nil 的 channel
  • 向已经关闭的 channel 写数据
 
Golang 的 slice 类型
 
type slice struct {
    array    unsafe.Pointer // 指向底层数组
    len      int       // 切片长度
    cap      int       // 底层数组容量
}
 
Go语言中的 slice 陷阱:如何避免常见的错误
  1. 在函数里修改切片元素的值,原切片的值也会改变(如果在函数内发生扩容,函数外的值不会改变)
  1. 调用 append 方法追加元素,如果切片的容量不够会引起切片扩容(内存分配和性能问题)
  1. 谨慎使用多个切片共享一个数组,会出现写冲突
  1. 当切片容量小于 1024 时,以 2 倍的规则扩容,否则以 1.25 倍的规则扩容
 
Golang 的 Map 类型
  1. 不是线程安全的,避免并发的读写 Map,或者使用读写锁 或者 sync.map 解决这个问题
  1. 使用了链地址法解决 Hash 冲突
  1. 使用了增量扩容解决负载因子大于 6.5(键数量/Bucket数量)过高的问题
 
Golang 数据类型的特点
数组:
  • 数组是值,将一个数组赋值给另一个,会拷贝所有的元素(如果你给函数传递一个数组,其将收到一个数组的拷贝,而不是它的指针)
  • 数组的大小是其类型的一部分。类型[10]int和[20]int是不同的。
切片:
切片持有对底层数组的引用,如果你将一个切片赋值给另一个,二者都将引用同一个数组(如果函数接受一个切片参数,对切片的元素所做的改动,对于调用者是可见的,好比是传递了一个底层数组的指针)
字典:
和切片类似,map持有对底层数据结构的引用。如果将map传递给函数,其对map的内容做了改变,则这 些改变对于调用者是可见的。
 
Golang 使用 defer 技巧
用于延迟函数的调用,常用于释放锁或其他资源
  • 多个defer出现的时候,它是一个“栈”的关系,也就是先进后出(一般写在函数开头捕获异常)
  • return 之后的语句先执行,defer 可以修改 return 的返回值
  • defer 函数的参数在执行时就已经确认了
  • 单个函数不能有过多 defer,影响执行的机制,导致效率下降
 
Golang panic 介绍
对于一些危险的操作,比如数组越界,会抛出 panic,提前结束程序执行,panic 的退出方式相对于 os.Exit() 比较优雅,支持使用 defer 和 recover 解决 panic
 
 
Golang recover 介绍
  • recover 必须要位于 defer 函数中
  • recover可以清除本函数产生的 panic,让上游函数以为一切正常执行
 
Go 变量作用域
花括号来控制变量的作用域,花括号中的变量是单独的作用域,同名变量会覆盖外层
 
Golang 控制结构
select 的特点
golang 提供的多路 I/O 复用机制 和 linux 的 select 机制类似
  • select 的每个 case 只能操作一个管道,根据管道的特性区分是否阻塞
  • 全部 case 都阻塞时陷入阻塞
  • 多个 case 不阻塞时,随机执行一个 case
  • 存在 default 时,永远不会阻塞
 
Golang 并发控制
一、使用 channel 控制协程同步
  • 实现简单,需要创建大量的协程
二、使用 WaitGroup 控制协程同步(实现原理信号量机制)
三、使用 Context 上下文控制子协程
  • 方便进行子协程的控制
四、使用 Mutex 控制并发
type Mutex struct {
    state int32  //32 位数字,低 3 位分别表示 3 种状态:唤醒状态、上锁状态、饥饿状态,剩下的位数则表示当前阻塞等待的 goroutine 数量。
    sema  uint32 //信号量
}
  • 类似于操作系统的 PV 原语操作,通过控制信号量 S 处理共享资源的抢占
  • 三种模式
  • 正常模式
  • 饥饿模式(解决一直被新来的 G 占用资源,队列里的 G 占有不了资源的情况)
  • 自旋模式(通过信号量唤起 G 比较耗资源,G 空转 CPU 去占有资源)
  • 其他细节
  • 多次 unlock 会出发 panic
 
posted @ 2023-08-22 20:55  _春华秋实  阅读(21)  评论(0编辑  收藏  举报