go学习记录——第八天

标准库

类似于fmtos的库在Golang中有150+个,被称为标准库(和python的什么math啊,os一样)(附录中D有详细的书里引用的包的列表,书的地址见 go学习记录——第三天

  • unsafe 包含一个打破 Golang “类型安全”的命令,一般不会使用(估计这辈子用到的概率比彗星撞地球还低)

  • syscall - os - os/exec

    • os 提供一个与平台无关的操作系统功能接口,采用类Unix的设计,旨在隐藏不同操作系统之间的差异,使得文件系统和操作系统对象保持一致(os属于对syscall进行了封装,整体会更加快速,简化复杂性,不必顾虑底层细节)。同时在错误处理方面采用了Go风格的错误处理机制,能够在函数返回时提供更多的错误信息而不是简单的错误码

    • os/exec 提供我们运行外部操作系统命令和程序的方式(就比如我们可以使用该包连接到PowerShell,然后执行go env命令)

      示例代码

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      package main

      import (
      "fmt"
      "log"
      "os/exec"
      )

      func main() {
      // 创建命令对象,指定要执行的PowerShell命令
      cmd := exec.Command("powershell.exe", "-Command", "go env")

      // 获取命令的输出
      output, err := cmd.Output()
      if err != nil {
      log.Fatal(err)
      }

      // 打印输出结果
      fmt.Println(string(output))
      }

    • syscall 底层的外部包,提供了操作系统底层调用的基本接口

示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package main
import (
"syscall"
)

const LINUX_REBOOT_MAGIC1 uintptr = 0xfee1dead
const LINUX_REBOOT_MAGIC2 uintptr = 672274793
const LINUX_REBOOT_CMD_RESTART uintptr = 0x1234567

func main() {
syscall.Syscall(syscall.SYS_REBOOT,
LINUX_REBOOT_MAGIC1,
LINUX_REBOOT_MAGIC2,
LINUX_REBOOT_CMD_RESTART)
}

这里需要说明的是原本的syscall.Syscall已经被弃用了,所以我改了一下,使用golang.org/x/sys/unix去完成类似的操作,代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package main

import (
"golang.org/x/sys/unix"
)

const (
LINUX_REBOOT_MAGIC1 = 0xfee1dead
LINUX_REBOOT_MAGIC2 = 672274793
LINUX_REBOOT_CMD_RESTART = 0x1234567
)

func main() {
unix.Reboot(LINUX_REBOOT_CMD_RESTART)
}
  • archive/tarcompress/gzip 压缩(解压缩)文件功能

  • fmt-io-bufio-path/filepath-flag

    • fmt 提供格式化输入输出功能(类似于C中的printfscanf,用法是fmt.Println fmt.Printf fmt.Scan fmt.Scanf
    • io 提供基本输入输出功能,主要围绕系统功能的封装(输入输出接口用法Reader Writer 主要是让不同的输入输出流能够通过统一的接口进行操作,常用函数有io.Copy io.ReadAll,主要是是哦不同的输入输出流之间复制数据或读取数据)
    • bufio 缓冲输入输出功能的封装(缓冲功能就是通过在读取和写入时使用缓冲区,减少对系统调用的次数,提高性能,常用的有bufio.Reader bufio.Writer
    • path/filepath 用来操作系统中目标文件名路径(提供了处理文件路径的函数,比如文件的扩展名、合并路径、获取绝对路径等,同时还支持跨平台的处理,比如对Windows和Linux之间的路径分隔符的差异进行处理)
    • flag 对命令行参数的相关操作(说白了就是对命令行参数的解析,能够允许开发者去定义和解析命令行参数,使得程序能够接收用户传入的参数,常用函数有flag.String flag.Int 等函数去定义不同类型的参数)
  • string - strconv - unicode - regexp - bytes

    • string 提供了对字符串进行操作的函数(比如对字符串查找、替换、分割、连接,判断字符串的前后缀,大小写转换等)
    • strconv 对字符串和基本数据类型进行转化(和python的数据类型转化类似,把字符串转为其他类型,其他类型转字符串)
    • unicode 提供对 Unicode 字符的处理功能(判断字符是否属于某个类型(字母啊数字啊这些),提供对字符的特定操作)
    • regexp 提供正则化表达式的支持,用于字符串的模式匹配和操作(查找、替换、验证字符串是否符合某个格式,支持复杂的正则表达式语法)
    • bytes 提供了对字节切片的操作(有点类似于 string但更注重于字节数据,提供对字节数据的高效处理)
  • math - math/cmath - math/rand - sort - math/big

    • math 提供基本数学函数和常数,支持浮点数的各种数学运算(比如三角函数呀(Sin Cos Tan)幂函数呀(Pow Sqrt Log)特殊函数呀(Inf NaN Hypot)都有)
    • math/cmath 提供处理复数的数学运算,有类似于 math 的函数,但是运用于复数类型的(有一说一除了高考那一道复数题我都八百年没听过这玩意了,好久远的存在)
    • math/rand 提供伪随机数生成器(和python著名的rand.random类似!我恨随机数,秋招鬼知道多少公司采用随机抽取简历)
    • sort 提供对切片和自定义集合的排序功能(这玩意我可太熟悉了,python是不是就得来个sort)
    • math/big 对大数进行运算(比如big.Int整数运算big.Float高精度浮点数运算big.Rat有理数运算)
  • container/list - ring - heap

    • container/list 主要是实现双向链表的数据结构(每个节点分别包含一个指向前和指向后的指针,常用方法有 PushBack(value)尾部添加元素 PushFront(value)头部添加元素 Remove(element)删除指定元素 Init()初始化链表)
    • container/ring 主要是实现环形链表的数据结构(最后一个节点指向第一个节点,适合循环访问元素的场景,常用方法有Next()返回下一个元素 Prev()返回前一个元素 Value获取或设置当前节点的值)
    • container/heap 主要是实现堆数据结构(用于实现优先队列,Golang的堆是基于sort.Interfacce实现的,允许用户定义自己的数据类型并实现相应的接口,常用方法Init(h)初始化堆 Push(h, x)向堆中加入元素 Pop(h)从堆中移除并返回最小(或最大)元素)
  • time - log

    • time 提供了对时间和日期的基本操作,包含了获取当前时间、时间格式化、时间计算等(time.Now()获取当前时间 time.Format()时间格式化为字符串 time.Parse()字符串解析为时间)
    • log 提供了简单的日志记录功能,允许开发者在程序允许时记录信息、错误和调试信息(log.Print() log.Println() log.Printf()记录信息 log.Fantal() log.Panic()记录错误并终止 log.SetPrifix() log.SetFlags()自定义日志输出格式
  • encoding/json/xml - text/template

    • encoding/json 用于处理JSON数据的编码和解码(json.Marshal(v interface{})将数据结构编码为JSON格式 json.Unmarshal(data []byte, v interface{})JSON数据解码到指定的Go的数据结构中
    • encoding/xml 提供了对xml数据的编码和解码功能(xml/Marshal(v interface{})将数据结构编码为XML格式 xml.Unmarshal(date []byte, v interface{})将XML数据编码到指定的Go数据结构中
  • net - net/http - html

    • net 用于网络编程呢的核心组件,提供了对网络I/O的基本操作,包括TCP、UDP、域名解析等(通过DialListen函数创建TCP或UDP链接,用net.LookupHost net.ResolveIPAddr等函数进行域名解析)
    • net/http 提供了HTTP客户端和服务器的实现,支持处理HTTP请求和响应(http.ListenAndServer启动HTTP服务处理客户端请求 hettp.Get http.Post等方法发起HTTP请求 http.Request http.ResponseWriter处理请求和响应)
  • runtime

    • 垃圾回收(GC)负责自动管理内存,回收不用的对象(这里我的理解是和python的类似,问了下AI给了个表)
    Go Python
    策略 三色标记-清除算法(分为黑白灰三类,白潜在,灰被检查,黑确定清除) 引用计数
    回收机制 并发回收(垃圾回收与程序执行并发进行) 标记-清除和分代回收
    相似之处 旨在自动管理内存,减少内存泄露风险 都是用标记-清除来识别不再使用的对象
    不同之处 并发操作 为即时处理,可能会导致出现程序暂停
    • 协程创建与管理,使用关键词go即可创建新的协程并进行调度控制等操作
  • reflect 实现通过程序允许时反射,让程序操作任意类型的变量

regexp包

简单模式的话可以直接使用Match()方法

1
ok, _ := regexp.Match(pat, []byte(searchIn))

变量ok将返回true or false,也可以使用MatchString()

1
ok, _ := regexp.MatchString(pat, searchIn)

下面将使用Compile()方法返回一个Regexp对象,并且涉及到匹配、查找、替换
示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package main

import (
"fmt"
"regexp"
"strconv"
)

func main() {
searchIn := "John: 2578.34 William: 4567.23 Steve: 5632.18"
pat := "[0-9]+.[0-9]+"

f := func(s string) string {
v, _ := strconv.ParseFloat(s, 32)
return strconv.FormatFloat(v*2, 'f', 2, 32)
}
if ok, _ := regexp.Match(pat, []byte(searchIn)); ok {
fmt.Println("Match Found")
}
re, _ := regexp.Compile(pat)
str := re.ReplaceAllString(searchIn, "##.#")
fmt.Println(str)
str2 := re.ReplaceAllStringFunc(searchIn, f)
fmt.Println(str2)
}

输出结果

1
2
3
Match Found
John: ##.# William: ##.# Steve: ##.#
John: 5156.68 William: 9134.46 Steve: 11264.36

除了Compile()我们还可以使用MustComplie(),这个方法会去检查我们正则的有效性,如果不合法时将panic()。换句话说,Compile()是可能返回错误的

锁和sync包

看到锁就想到了SQL里在面试的时候最怕也是最烦的锁相关问题,呕,但是继续学吧。

当我们遇到未知变量对我们已经固定好的线程顺序进行了修改(也就是资源抢夺,多个线程对同一变量使用进行抢夺),接下来就要说怎么去解决。

最经典的做法就是一次只允许一个线程对共享变量进行操作。当变量被一个线程改变时,我们就需要上锁(这不就是排他锁吗),直到我们该线程完成执行后解锁,其他线程才能进行访问。

在前面接触的map中就不存在锁机制,由于其需要考虑性能,所以并没有该类设计。所以map类型数据在并行被访问时就有可能出错。

一般来说的解决方案就是两种,sync.Mutexsync.RWMutex,但是在Go 1.9中引入了sync.map,一个专为并发设计的map

sync.Mutex

这个其实就是我们所说的排他锁(互斥锁),作用就是在临界区对同一时间能够进入的线程数进行控制(只允许一个)。

现在,假设info是一个需要上锁的在共享内存中的变量

1
2
3
4
5
import "sync"
type Info struct {
mu sync.Mutex
// ... other
}

然后如果想改变变量可以写

1
2
3
4
5
6
7
func Update(info *Info) {
info.mu.Lock()
// critical section:
info.Str = // new value
//end critical section
info.mu.UnLock()
}

还有一个例子是通过Mutex来实现一个可以上锁的共享缓冲器

1
2
3
4
type SyncedBuffer struct {
lock sync.Mutex
buffer bytes.Buffer
}

这个结构体的整体就是有一个排他锁去保证buffer能够在任何时刻只被一个goroutine访问,而缓冲器则是提供了动态增长的字节切片,适合高效的字节拼读和读取。

再回到我们的sync来,在这里面还有一个RWMutex锁,也是前面提到过的,主要作用就是在同一时间允许多线程访问但只允许单线程进行写操作。还有Lock()和一般的Mutex的作用是一样的。包里还有一个Once类型变量的方法once.Do(call)这个方法是确保被调用函数只能被调用一次(莫名想到了之前写的一个python的东西)

综上所述,一般情况下snyc包可以解决同一时间只能一个线程访问变量或map类型数据的问题,但是涉及到程序运行速度变慢或其他问题,我们又需要去思考其他解决办法,书里给的是通过goroutines or channels去解决,不过这部分得在后面才有,咱就先写一下,后面再说。

今天就先到这里吧,package的东西还有点多,明儿再学


go学习记录——第八天
https://www.lx02918.ltd/2024/11/19/go-study-eighth-day/
作者
Seth
发布于
2024年11月19日
许可协议