go读书笔记

1、判断浮点数是否为整数

func IsInt(bits uint32, bias int) {
	expoent := int(bits>>23) - bias - 23
	coefficient := (bits & ((1 << 23) - 1)) | (1 << 23)
	intTest := (coefficient & (1 << uint32((-expoent)-1)))
	fmt.Printf("\nExpoent:%d Coefficient: %d IntTest: %d\n",
		expoent,
		coefficient,
		intTest)
	if expoent < -23 {
		fmt.Println("Not INTEGER\n")
		return
	}
	if expoent < 0 && intTest != 0 {
		fmt.Println("Not INTEGER\n")
		return
	}
	fmt.Println("INTEGER\n")
}

2、为什么两个不同类型的数相乘和相除结果是整数或者浮点数

在Go语言规范中,对于常量表达式也制定了专门的规则。除了移位操作,如果操作数两边是不同类型的无类型常量,则结果类型的优先级为:整数(int)<符文数(rune)<浮点数(float)<复数(Imag)。根据此规则,上面两个常数之间相乘的结果将是一个浮点
数,因为浮点数的优先级比整数高。下面的例子结果为浮点数。

const third = 1/3.0

原因:因为浮点数优先级最高,所以结果就返回浮点数。


const zero = 1 / 3

原因:因为两个数都是整数,所以结果就返回整数。

常量分为命名常量与未命名常量。未命名常量只会在编译期间存在,因此不会存储在内存中。而命名常量存在于内存静态只读区,不能被修改。同时,Go语言禁止对常量取地址的操作。常量作为Go语言独特的功能之一,离不开编译时的解析。具体来说,隐式类型转换的规则为:有类型常量优先于无类型常量,当两个无类型常量进行运算时,结果类型的优先级为:整数(int)<符文数(rune)<浮点数(float)<复数(Imag)。

 3、字符串占字节问题

普通字母都只占据1字节,但是特殊的字符(例如大部分中文)会占据3字节

    var b = "GO语言"
	fmt.Println("字节:", len(b))  //8 两个字符是1*2=2 两个汉字是3*2=6  总共为8
	for i := 0; i < len(b); i++ {
		fmt.Printf("%x\n", b[i])
	}
fmt.Printf有一个特殊的格式化符#U可用于打印符文数十六进制
的Unicode编码方式及字符形状。如上例打印出:
var b = "GO语言"
	for index, runeValue := range b {
		fmt.Printf("%#U starts at byte position %d\n", runeValue, index)
	}

U+0047 'G' starts at byte position 0
U+004F 'O' starts at byte position 1
U+8BED '语' starts at byte position 2
U+8A00 '言' starts at byte position 5

在标准库strings包中包含字符查找、分割、大小写转换、trim修剪等数十个函数

//判断字符串s是否包含substr字符串
func Contains(s, substr string) bool
//判断字符串s是否包含chars字符串中的任一字符
func ContainsAny(s, chars string) bool
//判断字符串s是否包含符文数r
func ContainsRune(s string, r rune) bool
//将字符串s以空白字符分割,返回一个切片
func Fields(s string) []string
//将字符串s以满足f(r)==true的字符分割,返回一个切片 
func FieldsFunc(s string, f func(rune) bool) []string
//将字符串s以sep为分隔符进行分割,分割后字符串末尾去掉sep
func Split(s, sep string) []string

在标准库strconv包中,还包含很多字符串与其他类型进行转换的函数,如下所示:

//字符串转换为十进制整数 
func Atoi(s string) (int, error)
//字符串转换为某一进制的整数,例如八进制、十六进制
func ParseInt(s string, base int, bitSize int) (i int64, err error)
//整数转换为字符串
func Itoa(i int) string
//某一进制的整数转换为字符串,例如八进制整数转换为字符串
func FormatInt(i int64, base int) string

4、如何避免哈希碰撞?

一般有两种主要的策略:拉链法及开放寻址法。 

拉链法:将同一个桶中的元素通过链表的形式进行链接,这是一种最简单、最常用的策略。随着桶中元素的增加,可以不断链接新的元素,同时不用预先为元素分配内存。

缺点:需要存储额外的指针用于链接元素,这增加了整个哈希表的大小。

          同时由于链表存储的地址不连续,所以无法高效利用CPU高速缓存。

 开放寻址法:(OpenAddressing)所有元素都存储在桶的数组中。当必须插入新条目时,将按某种探测策略操作,直到找到未使用的数组插槽为止。当搜索元素时,将按相同顺序扫描存储桶,直到查找到目标记录或找到未使用的插槽为止。

Go语言中的哈希表采用的是开放寻址法中的线性探测(LinearProbing)策略,线性探测策略是顺序(每次探测间隔为1)的。由于良好的CPU高速缓存利用率和高性能,该算法是现代计算机体系中使用最广泛的结构

 5、Go语言中的可比较性

 ◎布尔值是可比较的。
◎ 整数值可比较的。
◎ 浮点值是可比较的。
◎ 复数值是可比较的。
◎ 字符串值是可比较的。
◎ 指针值是可比较的。如果两个指针值指向相同的变量,或者两个指针的值均为nil,则它们相等。
◎ 通道值是可比较的。如果两个通道值是由相同的make函数调用创建的,或者两个值都为nil,则它们相等。
◎ 接口值是可比较的。如果两个接口值具有相同的动态类型和相等的动态值,或者两个接口值都为nil,则它们相等。
◎ 如果结构的所有字段都是可比较的,则它们的值是可比较的。
◎ 如果数组元素类型的值可比较,则数组值可比较。如果两个数组对应的元素相等,则它们相等。
◎ 切片、函数、map是不可比较的。

6、Go语言为什么不支持并发的读写 

和其他语言不同的是,map并不支持并发的读写,map并发读写是初级开发者经常会犯的错误。下面的操作由于协程并发读写会报错为fatal error:concurrent map read and map write。

aa := make(map[int]int)
	go func() {
		for{
			aa[0]=5  //写
		}
	}()
	go func() {
		for  {
			_=aa[1] //读
		}
	}()

Go语言只支持并发读取map,因此下面的函数不会报错。

	aa := make(map[int]int)
	go func() {
		for {
			_=aa[0]  //读
		}
	}()
	go func() {
		for {
			_ = aa[1] //读
		}
	}()

Go语言为什么不支持并发的读写,是一个频繁被提起的问题。我们可以在Go官方文档的Frequently Asked Questions [3] 中找到问题的答案。官方文档的解释是:“map不需要从多个Goroutine安全访问,在实际情况下,map可能是某些已经同步的较大数据结构或计算的一部
分。因此,要求所有map操作都互斥将减慢大多数程序的速度,而只会增加少数程序的安全性。”即Go语言只支持并发读写的原因是保证大多数场景下的查找效率。

7、go中panic和recover嵌套容易踩坑

func a1() {
	defer b()
	panic("a panic")
}
func b() {
	defer fb()
	panic("b panic")
}
func fb() {
	panic("fb painc")
}

func main()  {
	a1()
}

 最终程序输出如下,先打印最早出现的panic,再打印其他的panic。后面会看到,每一次panic调用都新建了一个_panic结构体,并用一个链表进行了存储

panic: a panic
        panic: b panic
        panic: fb painc

嵌套panic不会陷入死循环,每个defer函数都只会被调用一次。当嵌套的panic遇到了recover时,情况变得更加复杂。将上面的程序稍微改进一下,让main函数捕获嵌套的panic。

func a1() {
	defer b()
	panic("a panic")
}
func b() {
	defer fb()
	panic("b panic")
}
func fb() {
	panic("fb painc")
}
func catch(funcname string) {
	if r := recover(); r != nil {
		fmt.Println(funcname, "recover:", r)
	}
}
func main() {
	defer catch("main")
	a1()
}

最终程序的输出结果为main recover:fb panic,这意味着recover函数最终捕获的是最近发生的panic,即便有多个panic函数,在最上层的函数也只需要一个recover函数就能让函数按照正常的流程执行。如果panic发生在函数b或函数fb中,则情况会有所不同。例如,将函数fb改写如下,内部的recover只能捕获由当前函数或其子函数触发的panic,而不能触发上层的panic

func fb() {
	defer catch("fb")
	panic("fb painc")
}

输出
fb recover: fb painc
panic: a panic
        panic: b panic

8、go中通过reflect反射结构体输出insert语言

package main

import (
	"fmt"
	"reflect"
)

type Student struct {
	Age  int
	Name string
}

func main() {
	fmt.Println("输出:", createQuery(Student{
		Age:  12,
		Name: "张三",
	}))
//输出
insert into Student values(12, '张三')
}
func createQuery(q interface{}) string {
	//判断类型为结构体
	if reflect.ValueOf(q).Kind() == reflect.Struct {
		//获取结构体名称
		t := reflect.TypeOf(q).Name()
		//查询语句
		query := fmt.Sprintf("insert into %s values(", t)
		v := reflect.ValueOf(q)
		//遍历结构体字段
		for i := 0; i < v.NumField(); i++ {
			//判断结构体类型
			switch v.Field(i).Kind() {
			case reflect.Int:
				if i == 0 {
					query = fmt.Sprintf("%s%d", query, v.Field(i).Int())
				} else {
					query = fmt.Sprintf("%s, %d", query, v.Field(i).Int())
				}
			case reflect.String:
				if i == 0 {
					query = fmt.Sprintf("%s%s", query, v.Field(i).String())
				} else {
					query = fmt.Sprintf("%s, '%s'", query, v.Field(i).String())
				}
				//剩下字段填充
			}
		}
		query = fmt.Sprintf("%s)", query)
		fmt.Println(query)
		return query
	}
	return ""
}

 通过Kind类型可以方便地验证反射的类型是否相同。

	num := 12.45
	equl := reflect.TypeOf(num).Kind() == reflect.Float64
	fmt.Println("kind is float64:", equl)

reflect.Value中的Interface方法以空接口的形式返回reflect.Value中的值。如果要进一步获取空接口的真实值,可以通过接口的断言语法对接口进行转换。下例实现了从值到反射,再从反射到值的过程。

var num float64 = 1.2345
	pointer := reflect.ValueOf(&num)
	value := reflect.ValueOf(num)
	convertPointer := pointer.Interface().(*float64)
	convertValue := value.Interface().(float64)

	fmt.Println(convertValue, convertPointer)

    输出:1.2345 0xc0000aa058

如果要转换的类型与实际类型不相符,则会在运行时报错。下例的反射中存储的实际是int指针,如果要转换为int类型,则会报错。

a := 56
	x := reflect.ValueOf(&a).Int()
	fmt.Println(x)

panic: reflect: call of reflect.Value.Int on ptr Value

如果反射中存储的是指针或接口,那么如何访问指针指向的数据呢?reflect.Value提供了Elem方法返回指针或接口指向的数据。

a := 56
	x := reflect.ValueOf(&a).Elem().Int()
	fmt.Println(x)

输出:56

 9、go中进程、线程、协程

操作系统调度到CPU中执行的最小单位是线程。在传统的单核(Core)CPU上运行的多线程应用程序必须交织线程,交替抢占CPU的时间片,如图所示。但是,现代计算机系统普遍拥有多核处理器。在多核CPU上,线程可以分布在多个CPU核心上,从而实现真正的并行处理。 

当发生线程上下文切换时,需要从操作系统用户态转移到内核态,记录上一个线程的重要寄存器值(例如栈寄存器SP)、进程状态等信息,这些信息存储在操作系统线程控制块
(Thread Control Block)中。当切换到下一个要执行的线程时,需要加载重要的CPU寄存器值,并从内核态转移到操作系统用户态。如果线程在上下文切换时属于不同的进程,那么需要更新额外的状态信息及内存地址空间,同时将新的页表(Page Tables)导入内存。 

调度方式:

协程是用户态的。协程的管理依赖Go语言运行时的调度器。同时,Go语言中的协程是从属于某一个线程的,协程与线程的对应关系为M:N,即多对多,如下图所示。Go语言调度器可以将多个协程调度到一个线程中,一个协程也可能切换到多个线程中执行。 

上下文切换的速度 :

协程的速度要快于线程,其原因在于协程切换不用经过操作系统用户态与内核态的切换,并且Go语言中的协程切换只需要保留极少的状态和寄存器变量值(SP/BP/PC),而线程切换会保留额外的寄存器变量值(例如浮点寄存器)。上下文切换的速度受到诸多因素的影
响,这里列出一些值得参考的量化指标:线程切换的速度大约为1~2微秒,Go语言中协程切换的速度比它快数倍,为0.2微秒左右 [3] 。

调度策略

线程的调度在大部分时间是抢占式的,操作系统调度器为了均衡每个线程的执行周期,会定时发出中断信号强制执行线程上下文切换。而Go语言中的协程在一般情况下是协作式调度的,当一个协程处理完自己的任务后,可以主动将执行权限让渡给其他协程。这意味着
协程可以更好地在规定时间内完成自己的工作,而不会轻易被抢占。当一个协程运行了过长时间时,Go语言调度器才会强制抢占其执行

栈的大小

线程的栈大小一般是在创建时指定的,为了避免出现栈溢出(Stack Overflow),默认的栈会相对较大(例如2MB),这意味着每创建1000个线程就需要消耗2GB的虚拟内存,大大限制了线程创建的数量(64位的虚拟内存地址空间已经让这种限制变得不太严重)。而Go
语言中的协程栈默认为2KB,在实践中,经常会看到成千上万的协程存在。同时,线程的栈在运行时不能更改,但是Go语言中的协程栈在Go运行时的帮助下会动态检测栈的大小,并动态地进行扩容。因此,在实践中,可以将协程看作轻量的资源。

10、go中并发和并行

并发与并行: 

在Go语言的程序设计中,有两个非常重要但容易被误解的概念,分别是并发concurrency)与并行(parallelism)。通俗来讲,并发指同时处理多个任务的能力,这些任务是独立的执行单元。并发并不意味着同一时刻所有任务都在执行,而是在一个时间段内,所有的任务都能执行完毕。因此,开发者对任意时刻具体执行的是哪一个任务并不关心。如图14-5所示,在单核处理器中,任意一个时刻只能执行一个具体的线程,而在一个时间段内,线程可能通过上下文切换交替执行。多核处理器是真正的并行执行,因为在任意时刻,可以同时有多个线程在执行。在实际的多核处理场景中,并发与并行常常是同时存在的,即多核在并行地处理多个线程,而单核中的多个线程又在上下文切换中交替执行。由于Go语言中的协程依托于线程,所以即便处理器运行的是同一个线程,在线程内Go语言调度器也会切换多个协程执行,这时协程是并发的。如果多个协程被分配给了不同的线程,而这些线程同时被不同的CPU核心处理,那么这些协程就是并行处理的。因此在多核处理场景下,Go语言的协程是并发与并行同时存在的。

但是,协程的并发是一种更加常见的现象,因为处理器的核心是有限的,而一个程序中的协程数量可以成千上万,这就需要依赖Go语言调度器合理公平地调度。 

总结:

进程是操作系统资源分配的基本单位,而线程是操作系统调度的基本单位。在一般情况下,线程是进程的组成部分,是不能脱离进程存在的。进程中的多个线程并发执行并共享进程的内存等资源。进程之间相对独立,不同进程具有不同的内存地址空间、操作系统资源描
述符等。协程是轻量级的线程,它依赖线程。由于协程具有更快的上下文切换速度、更灵活的调度策略、可伸缩的栈空间管理,借助简单的语法及运行时调度器的帮助,能够轻易编写出成千上万的协程,因此Go语言成为开发大规模、高并发项目的极佳选择。

11、日期转int保存数据库

//将前端传递过来的日期格式转换成int
location, _ := time.LoadLocation("Local")
birthDay, _ := time.ParseInLocation("2006-01-02", updateUserForm.Birthday, location)

//将int转日期

"birthday": time.Unix(int64(rsp.BirthDay), 0).Format("2006-01-02"),

12、时区的问题

func main() {
   location, _ := time.LoadLocation("Asia/shanghai")
   inputTime := "2029-09-04 12:02:33"
   layout := "2006-01-02 15:04:05"
   t, _ := time.Parse(layout, inputTime) //自动是UTC时间
   datetime := time.Unix(t.Unix(), 0).In(location).Format(layout)
   log.Printf("输入时间:%s,输出时间: %s", inputTime, datetime)
   //输入时间:2029-09-04 12:02:33,输出时间: 2029-09-04 20:02:33
   t, _ = time.ParseInLocation(layout, inputTime, location)//本地时区
   datetime = time.Unix(t.Unix(), 0).In(location).Format(layout)
   log.Printf("输入时间:%s,输出时间: %s", inputTime, datetime)
   //输入时间:2029-09-04 12:02:33,输出时间: 2029-09-04 12:02:33
}

13、go swagger的使用

PS D:\code\goproject\src\blog-service> go run main.go
[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.

[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
 - using env:   export GIN_MODE=release
 - using code:  gin.SetMode(gin.ReleaseMode)

[GIN-debug] GET    /ping                     --> main.main.func1 (3 handlers)
[GIN-debug] [WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.
Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details.
[GIN-debug] Environment variable PORT is undefined. Using port :8080 by default
[GIN-debug] Listening and serving HTTP on :8080
[GIN] 2023/01/23 - 22:34:52 | 404 |            0s |             ::1 | GET      "/"
[GIN] 2023/01/23 - 22:34:52 | 404 |            0s |             ::1 | GET      "/favicon.ico"
[GIN] 2023/01/23 - 22:34:58 | 200 |            0s |             ::1 | GET      "/ping"
exit status 0xc000013a
PS D:\code\goproject\src\blog-service> go get -u gopkg.in/natefinch/lumberjack.v2
PS D:\code\goproject\src\blog-service> swag init
2023/01/24 13:42:40 Generate swagger docs....
2023/01/24 13:42:40 Generate general API Info, search dir:./
2023/01/24 13:42:44 ParseComment error in file internal\routers\api\v1\tag.go :cannot find type definition: model.TagSwagger
PS D:\code\goproject\src\blog-service> swag init
2023/01/24 13:43:08 Generate swagger docs....
2023/01/24 13:43:08 Generate general API Info, search dir:./
2023/01/24 13:43:08 Generating model.Tag
2023/01/24 13:43:08 Generating model.Model
2023/01/24 13:43:08 Generating errcode.Error
2023/01/24 13:43:08 create docs.go at docs\docs.go
2023/01/24 13:43:08 create swagger.json at docs\swagger.json
2023/01/24 13:43:08 create swagger.yaml at docs\swagger.yaml
PS D:\code\goproject\src\blog-service> go get -u github.com/swaggo/swag/cmd/swag@v1.6.5
go: downloading github.com/swaggo/swag v1.6.5
go: downloading github.com/go-openapi/spec v0.19.4
go: downloading github.com/go-openapi/jsonreference v0.19.3
go: downloading golang.org/x/tools v0.5.0
go: downloading github.com/urfave/cli v1.22.12
go: downloading github.com/go-openapi/jsonreference v0.20.2
go: downloading github.com/go-openapi/spec v0.20.8
go: downloading github.com/go-openapi/swag v0.19.5
go: downloading github.com/PuerkitoBio/purell v1.2.0
go: downloading github.com/go-openapi/jsonpointer v0.19.6
go: downloading github.com/go-openapi/swag v0.22.3
go: downloading golang.org/x/text v0.6.0
go: downloading golang.org/x/net v0.5.0
go: downloading golang.org/x/sys v0.4.0
go get: installing executables with 'go get' in module mode is deprecated.
        To adjust and download dependencies of the current module, use 'go get -d'.
        To install using requirements of the current module, use 'go install'.
        To install ignoring the current module, use 'go install' with a version,
        like 'go install example.com/cmd@latest'.
        For more information, see https://golang.org/doc/go-get-install-deprecation
        or run 'go help get' or 'go help install'.
go get: added github.com/KyleBanks/depth v1.2.1
go get: added github.com/PuerkitoBio/purell v1.2.0
go get: added github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578
go get: added github.com/cpuguy83/go-md2man/v2 v2.0.2
go get: added github.com/ghodss/yaml v1.0.0
go get: added github.com/go-openapi/jsonpointer v0.19.6
go get: added github.com/go-openapi/jsonreference v0.20.2
go get: added github.com/go-openapi/spec v0.20.8
go get: added github.com/go-openapi/swag v0.22.3
go get: added github.com/josharian/intern v1.0.0
go get: added github.com/mailru/easyjson v0.7.7
go get: added github.com/russross/blackfriday/v2 v2.1.0
go get: added github.com/shurcooL/sanitized_anchor_name v1.0.0
go get: added github.com/swaggo/swag v1.6.5
go get: added github.com/urfave/cli v1.22.12
go get: upgraded golang.org/x/net v0.4.0 => v0.5.0
go get: upgraded golang.org/x/sys v0.3.0 => v0.4.0
go get: upgraded golang.org/x/text v0.5.0 => v0.6.0
go get: upgraded golang.org/x/tools v0.1.12 => v0.5.0
PS D:\code\goproject\src\blog-service> 
PS D:\code\goproject\src\blog-service> go get -u github.com/swaggo/gin-swagger@v1.2.0
go: downloading github.com/swaggo/gin-swagger v1.2.0
go: downloading github.com/goccy/go-json v0.10.0
go: downloading github.com/mattn/go-isatty v0.0.17
go: downloading github.com/pelletier/go-toml v1.9.5
go: downloading github.com/ugorji/go/codec v1.2.8
go: downloading github.com/ugorji/go v1.2.8
go: downloading golang.org/x/crypto v0.5.0
go: downloading github.com/go-playground/locales v0.14.1
go: downloading github.com/ugorji/go v1.1.5-pre
go get: upgraded github.com/go-playground/locales v0.14.0 => v0.14.1
go get: upgraded github.com/goccy/go-json v0.9.11 => v0.10.0
go get: upgraded github.com/mattn/go-isatty v0.0.16 => v0.0.17
go get: upgraded github.com/swaggo/swag v1.6.5 => v1.8.9
go get: upgraded github.com/ugorji/go/codec v1.2.7 => v1.2.8
go get: upgraded golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e => v0.5.0
PS D:\code\goproject\src\blog-service> go get -u github.com/swaggo/files
go: downloading github.com/swaggo/files v1.0.0
go get: added github.com/swaggo/files v1.0.0
PS D:\code\goproject\src\blog-service> go get -u github.com/alecthomas/template
go get: added github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751
PS D:\code\goproject\src\blog-service> 
 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值