Golang 学习笔记

基础

语法

只记录需要特别注意的点

Diff

  • 变量声明未使用会 CE

_ 的妙用

交换变量:a, b = b, a

空白标识符 _ 用于抛弃值,如值 5 在:_, b = 5, 7 中被抛弃。

_ 实际上是一个只写变量,你不能得到它的值。这样做是因为 Go 语言中你必须使用所有被声明的变量,但有时你并不需要使用从一个函数得到的所有返回值

goto 的妙用

对于收到程序错误,打印信息的重复代码可以用 goto 来简化代码。跳出多重循环不推荐。

    err := firstCheckError()
    if err != nil {
        goto onExit
    }
    err = secondCheckError()
    if err != nil {
        goto onExit
    }
    fmt.Println("done")
    return
onExit:
    fmt.Println(err)
    exitProcess()

函数妙用

import (
   "fmt"
   "math"
)

func main(){
   /* 声明函数变量 */
   getSquareRoot := func(x float64) float64 {
      return math.Sqrt(x)
   }

   /* 使用函数 */
   fmt.Println(getSquareRoot(9))

}

defer 语句

延迟执行,在函数执行结束后进行。

并且按照语句声明顺序,先进后出(类似栈)

map,无序集合

查看元素是否存在

/* 使用 key 输出 map 值 */
for country := range countryCapitalMap {
  fmt.Println("Capital of",country,"is",countryCapitalMap[country])
}

/* 查看元素在集合中是否存在 */
captial, ok := countryCapitalMap["United States"]
/* 如果 ok 是 true, 则存在,否则不存在 */
if (ok) {
  fmt.Println("Capital of United States is", captial)  
} else {
  fmt.Println("Capital of United States is not present") 
}

删除元素

delete(mp, "key")

错误处理

error 类型是一个借口类型,定义:

type error interface {
    Error() string
}

反射 Reflect

在不知道具体类型的情况下,可以用反射来更新变量值,查看变量类型

Typeof 返回类型

Valueof 返回值

通过反射修改值,需要传入变量地址修改

类的方法

Go 中没有类的概念,但可以为结构体定义方法。

定义某些方法的时候,需要声明接受者(类名和替代的名字),声明在 func 关键字与函数名之间。

import (
	"fmt"
	"math"
)

type Vertex struct {
	X, Y float64
}

func (v Vertex) Abs() float64 {
	return math.Sqrt(v.X*v.X + v.Y*v.Y)
}

func (v *Vertex) Scale(f float64) {     // 接受者类型是一个指针,没有返回值的方法
	v.X = v.X * f
	v.Y = v.Y * f
}

当接受者类型是一个指针的时候。

var v Vertex
v.Scale(5)  // OK
p := &v
p.Scale(10) // OK

这样的代码,v 的成员值也会发生变化,如果是 Scale(v, 5) 会发生编译错误,所以定义成指针类型接受者就变得很常见了

接受者类型为值的方法也是一样的道理。

一些好用的包中的函数

strings

strings.Filed(s string) 以空格分割字符串返回一个 string 数组。

Go 的并发

goroutine

动态栈

操作系统的线程一般都有固定的栈内存(通常为2MB),而 Go 语言中的 goroutine 非常轻量级,一个 goroutine 的初始栈空间很小(一般为2KB),所以在 Go 语言中一次创建数万个 goroutine 也是可能的。并且 goroutine 的栈不是固定的,可以根据需要动态地增大或缩小, Go 的 runtime 会自动为 goroutine 分配合适的栈空间。

调度器采用 GPM 调度模型

  • G: 表示goroutine,存储了goroutine的执行stack信息、goroutine状态以及goroutine的任务函数等;另外G对象是可以重用的。
  • P: 表示逻辑processor,P的数量决定了系统内最大可并行的G的数量(前提:系统的物理cpu核数>=P的数量);P的最大作用还是其拥有的各种G对象队列、链表、一些cache和状态。
  • M: M代表着真正的执行计算资源。在绑定有效的p后,进入schedule循环;而schedule循环的机制大致是从各种队列、p的本地队列中获取G,切换到G的执行栈上并执行G的函数,调用goexit做清理工作并回到m,如此反复。M并不保留G状态,这是G可以跨M调度的基础。

创建

在函数或方法前声明 go 关键字

同步

需要创建 sync.WaitGroup 类型变量

package main

import (
	"fmt"
	"sync"
)

var wg sync.WaitGroup

func hello() {
	defer wg.Done()     // 计数器 - 1
	fmt.Println("hello")
}

func main() {
	wg.Add(1)       // 计数器 + 1
	go hello()
	fmt.Println("END!")
	wg.Wait()
}

channel 通道类型

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

通道创立与关闭

声明一个, var 变量名 chan 元素类型 需要初始化否则默认为零值,这样声明是一个没有缓冲的通道(必须定义接收方的接受操作否则死锁)

注意:无缓冲不等于缓冲大小为 1。

初始化:make(chan 元素类型,[缓冲大小])

close(通道名) 关闭通道,关闭后的通道有以下特点:

  • 对一个关闭的通道再发送值就会导致 panic。
  • 对一个关闭的通道进行接收会一直获取值直到通道为空。
  • 对一个关闭的并且没有值的通道执行接收操作会得到对应类型的零值。
  • 关闭一个已经关闭的通道会导致 panic。

单向通道

以上都是双向通道,对于单向通道的声明:

  • <- chan int 只能接收
  • chan <- int 只能发送

select 多路复用

  • 可处理一个或多个channel的发送/接收操作
  • 如果多个case同时满足,select会随机选择一个执行
  • 对于没有case的select会一直阻塞,可用于阻塞 main 函数,防止退出

并发安全和互斥锁

互斥锁

互斥锁是一种常用的控制共享资源访问的方法,它能够保证同一时间只有一个 goroutine 可以访问共享资源。Go语言中使用sync包中提供的Mutex类型来实现互斥锁

import (
	"fmt"
	"sync"
)

var (
	x  int64
	wg sync.WaitGroup
	m  sync.Mutex // 互斥锁
)

func add() {
	for i := 0; i < 5000; i++ {
		m.Lock() // 修改x前加锁
		x = x + 1
		m.Unlock() // 改完解锁
	}
	wg.Done()
}
读写互斥锁

大多数场景读多写少

读写锁分为两种 读锁和写锁:

  • 当一个 goroutine 获取到读锁之后,其他的 goroutine 如果是获取读锁会继续获得锁。
  • 如果是获取写锁就会等待;而当一个 goroutine 获取写锁之后,其他的 goroutine 无论是获取读锁还是写锁都会等待。
import (
	"fmt"
	"sync"
	"time"
)

var (
	x  = 0
	wg sync.WaitGroup
	// lock sync.Mutex
	rwlock sync.RWMutex
)

// rwlock.RLock() 锁上读锁
// rwlock.RUnlock() 读锁解除
// rwlock.Lock() 写锁锁上
// rwlock.UnLock() 解除写锁
0 条评论
请不要发布违法违规有害信息,如发现请及时举报或反馈
还没有人评论呢,速度抢占沙发!
相关文章
  • 前言 作为强类型的静态语言,golang的安全属性从编译过程就能够避免大多数安全问题,一般来说也唯有依赖库和开发者自己所编写的操作漏洞,才有可能形成漏洞利用点,在本文,主要学习探讨一下golang的一...

  • 引言 最近做了一个需求,是定时任务相关的。以前定时任务都是通过 linux crontab 去实现的,现在服务上云(k8s)了,尝试了 k8s 的 CronJob,由于公司提供的是界面化工具,使用、查...

  • 背景 本文使用 Golang语言的SDK包 go.etcd.io/etcd/clientv3 实践etcd的租约、Watch等功能,并且实现分布式锁的业务场景。 etcd 租约 etcd过期时间可以通...

  • 一、算术运算符 运算符描述 + 相加 - 相减 * 相乘 / 相除 % 求余 代码示例: package main import "fmt" fun...

  • 背景 golang可以获取命令执行的输出结果,但要执行完才能够获取。 如果执行的命令是ssh,我们要实时获取,并执行相应的操作呢? 示例 func main() { user := "root"...

  • 概述建议先阅读 range, 非缓冲通道, 缓冲通道。range 除了可以遍历字符串、切片、数组等数据结构外,还可以遍历通道。语法规则和遍历其他数据结构不同,遍历通道时没有 索引 的概念,只有值,语法...

  • dongle 是一个轻量级、语义化、对开发者友好的 Golang 编码解码和加密解密库Dongle 已被 awesome-go 收录, 如果您觉得不错,请给个 star 吧github.com/gol...

  • 在Golang中,我们可以使用标准库中的encoding/json包来将JSON数据转换为结构体。本文将介绍如何使用该包将JSON数据转换为Golang结构体。 首先,让我们看一下JSON数据的格式。...

  • 一 kibana介绍 Kibana :是一个开源的分析和可视化平台,旨在与 Elasticsearch 合作。Kibana 提供搜索、查看和与存储在 Elasticsearch 索引中的数据进行交互...

  • 我现在的数据在sqlite中,保存在mac本地的一个文件中。用了electron+vue搭建了一个客户端。 我大概希望是这样的逻辑,先加载本地db文件,然后再获取数据。 这里就有一个问题,我怎么获取...

  • 概述使用的场景:在函数内部有很多重复性代码并且严重依赖上下文变量。此时可以在函数内部声明一个函数,专门用来处理重复性的代码。例子内部求和函数package main import "fmt" fu...

  • GO语言的环境安装 下载地址 Go下载 - Go语言中文网 - Golang中文社区 (studygolang.com) 安装 这里很简单,可以一直点下一步就可以了 环境搭建 我这里也是去D盘当...

  • 研发少闲月,九月人倍忙。又到了一年一度的“金九银十”秋招季,又到了写简历的时节,如果你还在用传统的Word文档寻找模板,然后默默耕耘,显然就有些落后于时代了,本次我们尝试使用云平台flowcv高效打造...

  • 输入某二叉树的前序遍历和中序遍历的结果,请构建该二叉树并返回其根节点。 假设输入的前序遍历和中序遍历的结果中都不含重复的数字。 示例 1: Input: preorder = [3,9,20,...

  • 本文介绍了Go 微服务体系发展与选型,过去一年Dubbo-go 社区的飞速发展以及对未来的展望。 一、Go 微服务体系发展与选型   随着微服务技术的快速发展,其在各个领域都形成了一系列...

  • 最近为了让python语言能够直接调用PaddleOCR的C++的动态链接库,针对本人已经开源的PaddleOCR项目https://gitee.com/raoyutian/paddle-ocrsha...

  • 前提准备与运行环境请参考:   在 Go 中 for 用来循环和迭代, Go 语言没有 while,do,until 这几个关键字,我们只能使用 for。这也算是件好事! 让我们来为一...

  • 面向对象介绍 面向对象和面向过程都是解决问题的一种思路。 面向过程: 是一种以过程为中心的编程思想,实现功能的每一步都是自己实现的。面向过程编程最易被初学者接受,其往往用一长段代码来实现指定功能...

  • 在gRPC里,客户端可以像调用本地方法一样直接调用其他机器上的服务端应用程序的方法,帮助你更容易创建分布式应用程序和服务。与许多RPC系统一样,gRPC是基于定义一个服务,指定一个可以远程调用的带有参...

  • 一 Zap日志介绍 Zap是在 Go 中实现超快、结构化、分级的日志记录。 Zap日志能够提供下面这些功能:   1、能够将事件记录到文件中,也可以在应用控制台输出   2、日志切割-可以...