golang复习总结

本文详细介绍了Golang的特性,包括变量、数组、切片、函数、接口、协程和并发处理。讨论了Go语言中引用类型与值类型的差异,强调了远程Debug、变量默认值以及如何在HTTP请求中使用协程。还提到了如何处理并发HTTP请求和数据库查询,利用协程池优化性能。
摘要由CSDN通过智能技术生成

Go安装包下载:https://go.dev/dl/
Go资源包搜索:https://pkg.go.dev/

在Windows环境下编译golang程序的时候提示:
"gcc"不是内部或外部命令,也不是可运行的程序或批处理文件”
这就很烦了,但是可以安装MinGW-w64编译器解决这个问题。
下载连接:https://sourceforge.net/projects/mingw-w64/files/

下载seh

在这里插入图片描述

很不错的go语言学习网站: https://haicoder.net/golang/golang-complex.html

在这里插入图片描述

golang保留字

一. 关键字(25个)

1. 程序声明

import 导入
package

2. 程序实体声明和定义

chan 通道
var 变量声明
const 常量声明
func 用于定义函数和方法
interface 定义接口
map 字典/map
struct 定义数据类型
type 类型声明

3. 程序流程控制

for
break
continue
select 选择流程
switch
case
default
defer 标识在函数退出之前执行
if
else
go 用于并行
goto
fallthrough 强制执行后面的case, 不能用在switch的最后一个分支。
range 遍历读取slice,map, channel数据
return 用于从函数返回
二. 内建常量(4个)
true false iota nil
//iota 是常量计数器, 使go的常量自增
三. 内建类型(20个)
int int8 int16 int32 int64
uint uint8 uint16 uint32 uint64 uintptr
float32 float64 complex128 complex64
bool byte rune string error
go语言类型分类
  1. 值类型数值类型,bool,string,数组,struct结构体
    变量直接存储值,内存通常在栈中分配, 修改值, 不会对源对象产生影响
  2. 引用类型指针,slice切片,管道chan,map,interface
    变量存储的是一个地址,这个地址对应的空间才真正存储数据值,内存通常在堆上分配,当没有任何变量引用这个地址时,该地>址对应的数据空间就成为一个垃圾,由GC来回收,同时,当地址指向的数据值改变时,会对源对象产生影响。
四. 内建函数(13个)
make len cap new append copy close delete
complex real imag
panic recover

Golang语言中的单引号和双引号的区别:
''用于表示字符类型
""用于表示字符串类型

函数定义的基本语法

func 函数名(形参 类型,形参 类型)(返回值 类型){
	执行语句
}
1. func可以当参数传递

	func mytype1(num1 int, num2 int)int{	
	}
	func mytype2(n1 func(int,int)int, num1 int, num2 int){
	}
	fun main(){
		num1 := 1
		num2 := 2
		mytype2(mytype1,num1,num2)
	}

2. 可变参数
	func args1(args... int){
		fmt.Println(args)
	}	
	func main(){
		args1(1,2,3,4)
	}
	
	args是一个切片类型的数据
	当你的包里面定义了一个init函数时,init会在main函数之前被调用

3. 匿名函数细节
	func (n1 int, n2 int)int{
		return n1 + n2
	}(n1,n2)
	
	或
	a := func (n1 int, n2 int)int{}(n1,n2)

4. 闭包
	func Addupper()func(int,int)int{
		return func(n int,n2 int)int{
			return n+n2
		}
	}

	func main(){
		add := Addupper()
		num3 := add(2,3)
		fmt.Println(num3)
	}

配置环境变量
vim /etc/profile(在文件最后追加)

export GO111MODULE=on
export GOROOT=/usr/local/go
export GOPATH=/opt/GoProject
export GOPROXY=https://goproxy.cn
export PATH=$PATH:$GOROOT/bin:$GOPATH/bin

更新环境变量
source /etc/profile
验证环境
go version
go env


go env -w GO111MODULE=on
go env -w GOROOT=C:/Program Files/Go  #这里是go安装包根目录
go env -w GOPATH=D:/www/svn/          #这里是项目主目录
go env -w GOPROXY=https://goproxy.cn,direct

//PowerShell 并执行
$env:GO111MODULE = "on"
$env:GOPROXY = "https://goproxy.cn"
//go env -w GOSUMDB=off                 #关闭包的有效性验证
//go env -w GOSUMDB="sum.golang.google.cn"
go mod tidy
Golang 的远程 Debug
  1. go环境安装(略)
  2. dvl 工具安装

cd $GOPATH
go install github.com/go-delve/delve/cmd/dlv@latest --安装dlv
ln -s $GOPATH/bin/dlv /usr/local/bin/dlv --配置软连接
dlv version
查一下进程号:ps aux | grep 进程号
ps -aux | grep go
启动dlv
centos 服务器上启动测试程序后,需要通过如下命令启用dlv工具,可以看到启动后 dlv在等待客户端连接
dlv attach 335 --headless --listen=:2345 --api-version=2 --accept-multiclient

#335 是 上述启动 go进程的id   2345 是服务监听的端口 这里写哪个端口 goland中就要配置哪个端口,其他参数可以通过 执行 dlv 查看帮助信息

API server listening at: [::]:2345
2021-12-26T13:06:55+08:00 warning layer=rpc Listening for remote connections >(connections are not authenticated nor encrypted)

测试程序请自己写个可以打断点的代码

bin通过go install编译时候,生成的可执行文件。
pkg通过go install编译时候,生成的包文件。
srcgo源码和依赖库。

每个 Go 工作区都包含三个基本文件夹, 创建工作区
cd %GOPATH%
mkdir bin\
mkdir src\
mkdir pkg\

bin:包含应用程序中的可执行文件。
src:包括位于工作站中的所有应用程序源代码。
pkg:包含可用库的已编译版本。 编译器可以链接这些库,而无需重新编译它们。

go mod init	     //生成 go.mod 文件
go mod download	 //下载 go.mod 文件中指明的所有依赖
go mod tidy	     //整理现有的依赖
go mod graph	 //查看现有的依赖结构
go mod edit	     //编辑 go.mod 文件
go mod vendor	 //导出项目所有的依赖到vendor目录
go mod verify	 //校验一个模块是否被篡改过
go mod why	     //查看为什么需要依赖某模块
对字符进行转义

\n:新行
\r:回车符
\t:制表符
\':单引号
\":双引号
\\:反斜杠

变量的默认值:
  1. 指针类型的,零值都是nil
  2. 值类型的变量,零值是其所在类型的零值
  1. map(引用类型)的零值是nil,
  2. int32类型的零值是0
  3. string类型的零值是""
  4. bool类型的零值是false
  5. 符合结构struct类型的零值是其每个成员的零值的组合
    struct的零值是Student1{0, “”}

struct类型和nil是两种不同的类型,不能比较。

golang数据类型

基本类型:数字、字符串和布尔值
聚合类型:数组和结构
引用类型:指针、切片、映射、函数和通道
接口类型:接口

1:基本类型
	负整型:包括intint8int16int32int64
	正整型:包括uintuint8uint32uint64byte 存放字符
		rune  代表一个 utf-8 字符
		uintptr  存放指针
	浮点型:包括float32float64
	复数类型:包括complex64complex128
	字符串类型:string
	布尔型:bool
	
	int类型的大小为 8 字节
	int8类型大小为 1 字节
	int16类型大小为 2 字节
	int32类型大小为 4 字节
	int64类型大小为 8 字节
	
2:聚合类型(复合类型)
	数组
	struct结构体
	
3:引用类型
	slice
	map
	channel
	pointer or 引用类型
	
4:接口类型
	io.Reader, io.Writer,error

类型的比较: https://www.jianshu.com/p/a982807819fa

类型比较的总结:

  1. 复合类型,只有每个元素(成员)可比较,而且类型和值都相等时,两个复合元素才相等
  2. slice,map不可比较,但是可以用reflect或者cmp包来比较
  3. func作为golnag的一等公民,也是一个类型,也不能比较。
  4. 引用类型的比较是看指向的是不是同一个变量
  5. 类型再定义(type A string)不可比较,是两种不同的类型
  6. 类型别名(type A = string)可比较,是同一种类型。
if块中的变量在外部无效
   if num := somenumber(); num < 0 {
       fmt.Println(num, "is negative")
   } else if num < 10 {
       fmt.Println(num, "has 1 digit")
   } else {
       fmt.Println(num, "has multiple digits")
   }

只在此 if 块内有效, 超出则显示 undefined: num

switch 包含强数据类型验证
//demo1
switch time.Now() {
case 0, 1, 2, 3:
    fmt.Print("case可含多个表达式,用逗号分隔")
    fallthrough //使逻辑进入到下一个紧邻的 case
case 9
default:
    fmt.Print(" 注意看:switch 可调用函数")
}

//demo2
switch {
    case "a":
        fmt.Println("可省略switch条件, 强制 switch 语句一直运行")
        //强制 switch 语句一直运行
数组和切片:

数组必备三元素var 变量名 [长度] 类型
切片不加长度 就是切片

var a []int                  //nil切片,和nil相等,一般用来表示一个不存在的切片
var b []int{}                //空切片,和nil不相等,一般用来表示一个空的集合
var c []int{1, 2, 3}         //有3个元素的切片,len和cap都为3
var d = c[:2]                //有2个元素的切片,len为2,cap为3
var e = c[:2:cap(c)]         //有2个元素的切片,len为2,cap为3
var f = c[:0]                //有0个元素的切片,len为0,cap为3
var g = make([]int, 3)       //创建一个切片,len和cap均为3
var h = make([]int, 3, 6)    //创建一个切片,len为3,cap为5
var i = make([]int, 0, 3)    //创建一个切片,len为0,cap为3

函数的参数一----因为它们属于引用类型

在golang语言中,哪些类型的变量作为参数传递给函数会导致变量的原始储值发生改变

  1. Slice 切片:当将一个切片作为参数传递给函数,并在函数内部修改切片元素时,原始切片的值会发生改变。
  2. Map 映射:当将一个映射作为参数传递给函数,并在函数内部修改映射的键值对时,原始映射的值会发生改变。
  3. Channel 通道:当将一个通道作为参数传递给函数,并在函数内部向通道发送或接收数据时,原始通道的值会发生改变。

这些类型被称为引用类型,它们存储的是指向底层数据结构指针。因此,当将它们作为参数传递给函数时,函数接收到的是指向原始变量指针,可以直接修改原始变量的值

其他类型,如整数、浮点数、字符串等基本类型,以及结构体和数组等复合类型,在函数内部对参数进行修改时,不会影响原始变量的储值。它们被称为值类型,函数接收到的是原始变量的拷贝副本

mapslice作为函数的参数– 成为值拷贝— 无法通过函数改变它本身的值

在这里插入图片描述

再看使用slice类型变量作为函数的参数差别:

在这里插入图片描述

函数的参数二----因为它们属于值类型

数组结构体作为函数的参数– 成为值拷贝— 无法通过函数改变它本身的值

在这里插入图片描述

函数
函数使用指针引用内存地址, 改变外部变量

在这里插入图片描述

二维数组的定义:

先定义一个双胞胎, 每个双胞胎里面有3个元素 = 定义一个二行3列的数组
在这里插入图片描述

在这里插入图片描述

实例为一个 3 行 4 列的二维数组

在这里插入图片描述

访问 map 中不存在的 key

// 正确示例
func main() {
    x := map[string]string{"one": "2", "two": "", "three": "3"}
    if _, ok := x["two"]; !ok {
        fmt.Println("key two is no entry")
    }
}

下面错误的 key 检测方式
func main() {
    x := map[string]string{"one": "2", "two": "", "three": "3"}
    //Go 则会返回元素对应数据类型的零值,比如 nil、'' 、false 和 0 , 不存在它也会有返回值, 坑
    if v := x["two"]; v == "" {
        fmt.Println("key two is no entry")  // 键 two 存不存在都会返回的空字符串
    }
}

在多行 array、slice、map 语句中不能缺少 , 号

func main() {
    x := []int {
        1,
        2    // syntax error: unexpected newline, expecting comma or }
    }
    y := []int{1,2,}    
    z := []int{1,2}    
    // ...
}

不导出的 struct 字段无法被 encode

//以小写字母开头的字段成员是无法被外部直接访问的,所以 struct 在进行 json、xml、gob 等格式的 encode 操作时,这些私有字段会被忽略,导出时得到零值:
func main() {
    in := MyData{1, "two"}
    fmt.Printf("%#v\n", in)    // main.MyData{One:1, two:"two"}

    encoded, _ := json.Marshal(in)
    fmt.Println(string(encoded))    // {"One":1}    // 私有字段 two 被忽略了

    var out MyData
    json.Unmarshal(encoded, &out)
    fmt.Printf("%#v\n", out)     // main.MyData{One:1, two:""}
}
Golang语言的接口
定义接口:
type MyInterface interface {
    Method1() int
    Method2(string) bool
}

该接口包含两个方法Method1和Method2,分别返回intbool类型。
实现接口:指编写一个类型,实现接口中定义的所有方法。
type MyStruct struct {}

func (m *MyStruct) Method1() int {
    // 实现Method1方法
    return 1
}

func (m *MyStruct) Method2(str string) bool {
    // 实现Method2方法
    return len(str) > 0
}
使用接口:
使用接口就是将接口类型的变量指向实现该接口的类型的实例。
var obj MyInterface
obj = &MyStruct{}

然后调用接口
x := obj.Method1()
y := obj.Method2("hello")

注意,调用接口的方法时,实际上是调用实现该接口的类型的方法,这些方法可能会以不同的方式实现,但它们都必须满足接口定义的方法签名

Golang如何将http请求修改为https请求
  1. 首先必须下载openssl用于生成ssl证书下载地址: http://slproweb.com/products/Win32OpenSSL.html

  2. 配置环境变量 D:\Openssl\OpenSSL-Win64\bin

  3. 进入D盘根目录cmd执行命令:
    openssl genrsa -out ./server.key 2048
    openssl req -new -x509 -key ./server.key -out ./server.pem -days 365
    注意:第一个输入框填cn后面的回车就可以了。(国家名称)

  4. 将生成的两个文件拖入main.go文件同级目录在这里插入图片描述

  5. 这里我们使用的是gin框架,所以只需要修改一行代码即可
    r.RunTLS(":8000","./server.pem","./server.key") #将r.run(":8000")修改即可

Golang语言中 var 和 type 一个结构的区别

这种叫匿名结构体(Anonymous Structure), 一般叫临时变量, 以用于临时使用, 通过 new() 创建后返回的是它的地址, new它返回的永远是类型的指针,指针指向分配类型的内存地址,若是字串会是"",number 会是 0,channel, func, map, slice 等等则会是 nil

在这里插入图片描述

下面是go项目的虚拟机挂载情况

mkdir /opt/GoProject/bin
mkdir /opt/GoProject/pkg
mkdir /opt/GoProject/src
mkdir /opt/GoProject/Setup
vmhgfs-fuse .host:/bin/ /opt/GoProject/bin -o subtype=vmhgfs-fuse,allow_other,nonempty
vmhgfs-fuse .host:/pkg/ /opt/GoProject/pkg -o subtype=vmhgfs-fuse,allow_other,nonempty
vmhgfs-fuse .host:/src/ /opt/GoProject/src -o subtype=vmhgfs-fuse,allow_other,nonempty
vmhgfs-fuse .host:/Setup/ /opt/GoProject/Setup -o subtype=vmhgfs-fuse,allow_other,nonempty

#下面是go的环境变量
export GO111MODULE=on
export GOROOT=/opt/go
export GOPATH=/opt/GoProject
export PATH=$PATH:$GOROOT/bin:$GOPATH/bin

编译到Windows端执行程序 ---- 交叉编译在这里插入图片描述

goland 未解析的依赖项
这个错误的处理办法是需要勾选go模块集成。
在这里插入图片描述

关于协程
至少20个最常用的golang的协程使用场景

以下是20个最常见的Golang协程使用场景:

  1. 并发处理多个HTTP请求
  2. 通过并发处理I/O操作来提高程序的吞吐量
  3. 并发处理大型数据集,例如在MapReduce算法中
  4. 在高并发环境下,使用协程避免阻塞并提高程序的响应速度
  5. 在后台进行任务,例如日志记录或数据备份
  6. 实时处理消息队列中的消息
  7. 响应事件驱动的编程模型,例如在GUI编程中
  8. 并发处理多个数据库连接
  9. 执行非阻塞的系统调用,例如读取文件或者网络连接
  10. 非阻塞等待多个通道事件的发生
  11. 定时执行任务,例如执行定时备份
  12. 对多个数据源进行并发读取,例如读取多个文件
  13. 在高负载的情况下使用协程来避免服务器过载
  14. 并发处理多个任务,例如并行化测试或压力测试
  15. 在分布式系统中,通过协程来管理不同节点之间的通信
  16. 在运行时动态创建协程来处理任务
  17. 使用协程处理不确定的任务,例如在自动化测试中
  18. 在分时系统中并行执行多个任务
  19. 并发处理多个任务,以减少响应时间
  20. 使用协程处理多个输入源,例如处理多个传感器数据流。

请注意,这只是一些常见的场景,实际上Golang协程的使用场景非常广泛,可以根据应用程序的需要进行任何类型的并发处理。

golang并发处理多个HTTP请求, 哪种情况下才会出现呢? 请举例并说明

在处理多个HTTP请求时,Golang中的协程可以并行处理这些请求,从而提高程序的吞吐量和性能。以下是一些可能需要并发处理多个HTTP请求的情况:

  1. 在Web应用程序中,处理多个客户端请求时,可以使用协程来并发处理这些请求,以提高并发性能。例如,在一个电子商务网站中,当多个客户同时访问该网站时,可以使用协程来并行处理每个请求,以提高响应速度和吞吐量。
  2. 在网络爬虫中,当需要爬取多个网页时,可以使用协程来并发地爬取这些网页,从而提高爬取速度。例如,当爬取一个包含数百个页面的网站时,可以使用协程来并行处理这些页面的请求,以提高爬取速度。
  3. 在API请求代理中,当需要转发多个API请求时,可以使用协程来并行处理这些请求。例如,当需要将多个API请求发送到不同的后端服务中时,可以使用协程来并行处理这些请求,从而提高代理服务的性能。
  4. 在处理大量用户上传文件时,可以使用协程并发地处理这些文件,以提高处理速度。例如,当一个网站允许用户上传大量文件时,可以使用协程来并行处理这些上传请求,以提高网站的性能。
  5. 需要注意的是,在并发处理多个HTTP请求时,需要小心处理并发的资源竞争问题,例如共享的数据结构或数据库连接。需要使用合适的同步机制来保证并发访问的正确性。
以下是另外10个最常见的Golang协程使用场景:
  1. 在实时通信应用程序中,使用协程来处理多个用户的聊天请求。例如,在一个在线游戏中,可以使用协程来并发地处理每个玩家的聊天请求,以提高游戏的响应速度。
  2. 在数据处理应用程序中,使用协程来并发地处理多个数据文件的读取和处理。例如,在一个数据分析应用程序中,可以使用协程来并发地读取和处理多个数据文件,以提高数据处理速度。
  3. 在多任务并行编程中,使用协程来并发地执行多个任务。例如,在一个计算密集型应用程序中,可以使用协程来并行处理多个任务,以提高应用程序的性能。
  4. 在流媒体应用程序中,使用协程来并发地处理多个音视频流。例如,在一个视频会议应用程序中,可以使用协程来并行处理多个参与者的音视频流,以提高会议质量。
  5. 在并发访问数据库时,使用协程来并发地执行多个数据库查询。例如,在一个电子商务网站中,可以使用协程来并发地执行多个数据库查询,以提高网站的响应速度。
  6. 在网络服务器应用程序中,使用协程来并发地处理多个TCP连接。例如,在一个聊天服务器应用程序中,可以使用协程来并行处理多个用户的TCP连接,以提高服务器的性能。
  7. 在处理大量请求时,使用协程来并发地执行任务队列。例如,在一个任务调度应用程序中,可以使用协程来并发地执行任务队列中的任务,以提高调度器的性能。
  8. 在并发处理多个文件时,使用协程来并发地处理多个文件读写操作。例如,在一个文件同步应用程序中,可以使用协程来并行处理多个文件的读写操作,以提高同步速度。
  9. 在处理多个用户请求时,使用协程来并发地执行多个后台任务。例如,在一个电子邮件服务应用程序中,可以使用协程来并行处理多个用户的邮件发送请求,以提高邮件发送速度。
  10. 在多个服务之间进行通信时,使用协程来并发地执行通信操作。例如,在一个微服务架构的应用程序中,可以使用协程来并发地执行多个服务之间的通信,以提高整个应用程序的性能。

需要注意的是,Golang协程非常适合处理高并发的场景,可以应用于各种类型的应用程序和场景中。

首先定义一个数据库查询方法; 然后当一个http请求进来, 我们先获取一个空闲的协程, 然后调用数据库查询方法; 如果当前没有空闲协程并且协程池中最多不能超过10个协程的情况下创建新的协程后调用数据库查询方法, 否则等待新的空闲协程后调用数据库查询方法. 每一个协程使用完成后都应该释放.
// 定义数据库查询方法
func queryFromDB() {
	// TODO: 实现具体的数据库查询逻辑
}

func main() {
	// 定义协程池
	pool := make(chan struct{}, 10)
	
	// 处理HTTP请求
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		// 尝试获取一个空闲协程
		select {
		case pool <- struct{}{}:
			// 获取到空闲协程,使用该协程执行查询
			go func() {
				queryFromDB()
				<-pool // 查询完成后释放协程
			}()
		default:
			// 没有空闲协程,检查当前协程池中的协程数是否超过了10个
			if len(pool) < cap(pool) {
				// 创建新的协程执行查询
				go func() {
					pool <- struct{}{}
					queryFromDB()
					<-pool // 查询完成后释放协程
				}()
			} else {
				// 协程池已满,等待空闲协程
				<-pool
				go func() {
					queryFromDB()
					<-pool // 查询完成后释放协程
				}()
			}
		}
	})
	
	// 启动HTTP服务
	http.ListenAndServe(":8080", nil)
}

在这个实现中,我们首先定义了一个协程池 pool,它的容量是 10,代表了最多可以同时运行的协程数。每次处理一个 HTTP 请求时,我们先尝试从协程池中获取一个空闲的协程。如果能获取到,就使用该协程执行数据库查询。查询完成后,我们将该协程释放,以便其它请求可以使用它。

如果协程池中没有空闲的协程,我们检查当前协程池中的协程数是否超过了 10 个。如果没有超过 10 个,我们就创建新的协程执行查询。如果已经有 10 个协程在运行,我们就等待其中任意一个协程结束,然后将其释放,以便新的请求可以使用它。无论是新创建的协程还是空闲的协程,都要在查询完成后将自己释放回协程池中。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

kentrl

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值