Golang初相识 |青训营笔记

100 阅读6分钟

这是我参与「第五届青训营」伴学笔记创作活动的第一天

Go语言特性

作为一门强类型的静态语言,与众多其他流行语言相比较:

1.Go没有沉重的历史包袱,在包管理和环境管理上和“万人迷”Python一样出色。

2.简化后的语法对熟悉C/C++的我来说,像是在写另一种Python, 效率极大提高。

3.标准库做了很多的“最佳实践”,省去了找功能包的不便。

4.能够交叉编译,这也是其跨平台能力强的表现之一。

5.由于是静态编译,不需要像Java一样需要JVM虚拟机,这节省了一部分程序运行的开销,提高了性能。但是相应的runtime被编译进了最后的可执行文件,所以程序体积会变大,不过目前空间还是比时间廉价的

6.Go有自己的垃圾回收机制(建立在Go的runtime上),加上defer(语法糖,有部分性能影响)的加入,极大的减轻了开发人员的负担。

7.得益于import的使用(这里必须吐槽下C++20标准才有的import, 不知道啥时候能大范围用起来...)和更少的关键字,类似的项目Go的编译速度相对于C++是真的快些,尤其是在做简单更改后的再次编译,性能卓越(摸鱼时间减少)。

8.设计实现了协程,为高并发编程提供了良好的支撑,实际上使用的时候坑还是蛮多的。<\p>

基本语法与C++极其类似,对于切片的操作还有循环里的range的使用与python类似,内置类型少,比较有意思的是const没有具体的类型,以及go拥有更直观的错误处理方式,其他的基本语法和功能很多都在其他语言中出现过。最最有趣的是类型声明是倒着写的,这个一开始真的有些别扭,原因这篇博文有讲

课程项目简述

猜数字

一个简单的小游戏, 熟悉下语法,技术要点就是我对go的体验非常好的地方,包括直观的错误处理方式,defer的使用,还有字节流输入和处理的概念(真的很像C/C++),让我很有熟悉感,在高级语言中使用底层的概念,还是有自己的知识没白学的感叹。最后把用字节流的输入方式改为fmt.Scanf的作业比较基础,对比buffer的处理方式,scnaf更便捷,和C一致。

go语言字典

个人很喜欢的一个项目,深度体验了go语言标准库的强大之处,网络编程的体验感(相对于C++)真的是提升一大截。课程作业在这个项目上有两个要求,第一个要求是再找一个翻译的api来用,代码部分的处理照葫芦画瓢,使用了两种代码生成的技术:curl命令转go语言操作json转go结构体,效率提升杠杠的。找api真没少花时间,大部分网站都做了对单个连接的调用次数和连接时长的限制,个人找的是某狗的api大家可以拿去玩玩,这部分代码如下

//golang
//sou gou struct
type DictResponse2 struct {
   Code    int    `json:"code"`
   Message string `json:"message"`
   Data    struct {
      Phonetic []struct {
         Filename string `json:"filename"`
         Text     string `json:"text"`
         Type     string `json:"type"`
      } `json:"phonetic"`
      Paraphrase []struct {
         Pos   string `json:"pos"`
         Value string `json:"value"`
      } `json:"paraphrase"`
      Translation struct {
         From      string `json:"from"`
         TransText string `json:"trans_text"`
         To        string `json:"to"`
      } `json:"translation"`
      FirstInterpretation string `json:"firstInterpretation"`
   } `json:"data"`
}

//sou gou query
func query2(word string) {
   client := &http.Client{}
   var data = strings.NewReader(`from=en&to=zh-CHS&query=` + word)
   req, err := http.NewRequest("POST", "https://fanyi.sogou.com/api/transpc/text/transword", data)
   if err != nil {
      log.Fatal(err)
   }
   req.Header.Set("Accept", "application/json, text/plain, */*")
   req.Header.Set("Accept-Language", "zh-CN,zh;q=0.9")
   req.Header.Set("Connection", "keep-alive")
   req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
   req.Header.Set("Cookie", "ABTEST=6|1673780657|v17; SNUID=84BFC9127C788E3611FDA84C7D64E58B; IPLOC=CN1402; SUID=F9C2B46ECE52A00A0000000063C3DDB1; wuid=1673780657185; FQV=ba28d407d81e04b19204703408a0e53a; translate.sess=f7e803b3-daaf-4dfe-97db-350e63bf869e; SUV=1673780638629; SGINPUT_UPSCREEN=1673780638644")
   req.Header.Set("Origin", "https://fanyi.sogou.com")
   req.Header.Set("Referer", "https://fanyi.sogou.com/text?keyword=hello&transfrom=auto&transto=zh-CHS&model=general")
   req.Header.Set("Sec-Fetch-Dest", "empty")
   req.Header.Set("Sec-Fetch-Mode", "cors")
   req.Header.Set("Sec-Fetch-Site", "same-origin")
   req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36")
   req.Header.Set("sec-ch-ua", `"Not_A Brand";v="99", "Google Chrome";v="109", "Chromium";v="109"`)
   req.Header.Set("sec-ch-ua-mobile", "?0")
   req.Header.Set("sec-ch-ua-platform", `"Windows"`)
   resp, err := client.Do(req)
   if err != nil {
      log.Fatal(err)
   }
   defer func(Body io.ReadCloser) {
      err := Body.Close()
      if err != nil {

      }
   }(resp.Body)
   var dictResponse2 DictResponse2
   err = json.NewDecoder(resp.Body).Decode(&dictResponse2)
   if err != nil {
      log.Fatal(err)
   }
   fmt.Println("\nSou Gou Dict:")
   fmt.Println(word)
   fmt.Println("UK:", `[`+dictResponse2.Data.Phonetic[0].Text+`]`, "US:", `[`+dictResponse2.Data.Phonetic[1].Text+`]`, dictResponse2.Data.Translation.TransText)
   for _, st := range dictResponse2.Data.Paraphrase {
      fmt.Println(st.Pos, st.Value)
   }
   wg.Done()
}

query里面的wg是一个sync.waitgroup对象,主要是用来完成第二个任务的,用来做协程间的同步,即并发的对两个翻译引擎进行请求,这里主要使用了go的coroutine的协程并发技术,协程不同于线程,切换开销更小,但是使用上陷阱也还是很多,由于这里的实现只有读的需求所以没必要加锁,具体使用到协程的代码如下

func main() {
   wg.Add(2)
   if len(os.Args) != 2 {
      _, err := fmt.Fprintf(os.Stderr, `usage: simpleDict WORD
example: simpleDict hello
      `)
      if err != nil {
         log.Fatal(err)
      }
      os.Exit(1)
   }
   word := strings.ToLower(os.Args[1])
   go query(word)
   go query2(word)
   wg.Wait()//只有当delta的值为0才解除block
   fmt.Println("查询结束......")
}

上面需要wait的原因是main()本身在运行时就会有一个goroutine, 如果在其他goroutine结束前main()结束了运行,那么其他未执行的goroutine都会结束运行,所以需要wg这个对象来做同步,这里可以看下操作系统课程里面的一些算法,都是比较基本的内容。

最后

不多不说,能完全零基础快速上手go有go语言本身的简单性的原因,但是另一方面go的相当多的功能都基本都在我熟悉的编程语言中出现过,就算是goroutine的概念实际上也是很多基础计算机知识的具体实践。想来大一的时候看到C++之父说的每个开发者最好掌握3 - 7门编程语言,我还觉得的是否会贪多嚼不烂,目前来看编程语言实际上是计算机科学基础知识的一些实践品,真正重要的还是他们背后所代表的计算机思想和前人走了无数弯路总结出来的宝贵知识。

最后推荐大家有机会的可以看下《代码之髓》。一本小册子,可以很快的读完,比较简单讲了编程语言的核心,以及如何系统化的掌握一门编程语言,内容略微过时,真希望能出第二版,貌似第一版已经成了孤本了,二手都很贵。

引用

1.进程、线程、协程、goroutine区别