加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 大数据 > 正文

Go语言实战读书笔记

发布时间:2020-12-16 18:07:42 所属栏目:大数据 来源:网络整理
导读:Go语言实战读书笔记 第二章 通道(channel)、映射(map)和切片(slice)是引用类型。引用类型的对象需要使用make函数进行构造。 在Go程序中,如果函数main()返回,整个程序就终止了。这时,Go会关闭全部goroutine。 使用for range迭代切片时,每次迭代前会

Go语言实战读书笔记

第二章

通道(channel)、映射(map)和切片(slice)是引用类型。引用类型的对象需要使用make函数进行构造。

在Go程序中,如果函数main()返回,整个程序就终止了。这时,Go会关闭全部goroutine。

使用for range迭代切片时,每次迭代前会返回两个值:元素在切片中的索引和元素副本。

Go支持闭包。

解析JSON示例:

type Feed struct {
    Name string `json:"site"`
    URI string `json:"link"`
    Type string `json:"type"`
}

file,_ := os.Open(filename)
var feeds []*Feed
json.NewDecoder(file).Decode(&feeds)

声明接口示例:

type Matcher interface {
  Search(feed *Feed,searchTerm string) ([]*Result,error)
}

使用指针作为接受者声明的方法,只能由指针调用。使用值作为接受者声明的方法,值和指针都可以调用。当使用值调用时,传入方法的是值的副本。

如果使用for range对通道进行迭代时,当通道关闭后,迭代会终止。

除了main包外,Go包应当与其所在目录同名。

第三章

Go在寻找包时,先从$GOROOT目录下寻找,接着在$GOPATH目录下寻找。

命名导入:

import myfmt "mylib/fmt"
import _ "mylib/init"

包的初始化。每个包可以包含多个init函数,这些函数将在main.main()之前执行。

Go工具

构建:

go build hello.go  # 构建指定文件。
go build # 构建当前目录。
go build github.com/goinaction/code/chapter3/wordcount # 构建指定包。
go build github.com/goinaction/code/chapter3/... # 构建指定目录下的全部包。

清理构建文件:

go clean hello.go

构建并执行:

go run hello.go

检查代码中的常见错误:

go vet
go vet main.go
go vet .

格式化代码:

go fmt
gofmt -l -w -s .

查看文档:

go doc tar
godoc -http=:6060

Go源代码文档

函数文档示例:

// Retrieve 连接到配置库,收集各种链接设置、用户名和密码。这个函数成功时
// 返回 config 结构,否则返回一个错误。
func Retrieve() (config,error) {
    // ...
}

包文档示例:

// 包 usb 提供了用于调用 USB 设备的类型和函数。
package usb
// ...

第四章

声明数组:

var a1 [5]int
var a2 = [3]int{1,2,3}
var a3 = [...]int{1,3}
var a4 = [3]*int{0: new(int),1: new(int)}

数组赋值会复制元素:

a1 := [3]string{"a","b","c"}
var a2 [3]string
a2 = a1

多维数组:

var a1 [4][2]int
a2 := [4][2]int{{10,11},{20,21},{30,31},{40,41}}
a3 := [4][2]int{1: {0: 20},3: {1: 41}}
var a4 [2]int = a3[1]

不要用数组作为函数参数。这么做会复制大量对象。要使用切片。

建立切片:

s1 := make([]int,5)
s2 := make([]int,3,5)
s3 := []{1,3}
s4 := []string{99: ""}
s5 := s1[1:3] # s5和s1共享同一个底层数组
s6 := s1[2:3:4] # s6是长度为1,容量为2的切片

切片会包含一个底层数组。

切片和数组的区别在于,[]中没有数字。

对于底层数组容量是k的切片s[i:j],其长度是j-i,容量是k-i。

在对切片进行迭代时,返回的是切片元素的副本。每次迭代时,值副本的地址是相同的。

多维切片:

s := [][]int{{10},{100,200}}

切片包含地址指针、长度和容量。在64位计算机上,一个切片占用24字节。复制切片时不会复制底层数组,因此将切片作为参数传递给函数,开销很小。

切片函数:

cap(s) # 容量
len(s) # 长度
append(s,element)

映射使用了散列表。在每次迭代中,各元素返回的次序可能不同。

建立映射:

m1 := make(map[int]int)
m2 := map[string]string{"Red": "#da1337","Orange": "#e95a22"}

映射的键必须是可以使用==比较的对象。函数、切片、以及包含切片的对象,由于具有引用语义,不能作为映射的键。

从映射中获取键对应的值时,如果键不存在,会返回零值。

映射函数:

delete(m,"key")

第五章

Go是静态类型语言。

自定义类型字面值:

type user struct {
  name string
  email string
}

type admin struct {
  person user
  level string
}

u1 := user{"Lisa","lisa@abc.com"}
u2 := user{name: "Lisa",email: "lisa@abc.com"}
a1 := admin{
  person: user{"Lisa","lisa@abc.com"}
  level: "super"
}

以指针为接收者的函数只能通过指针调用。以值为接收者的函数可以通过值或指针调用。对于以值为接收者的函数,函数域中的接收者是值的副本,即使通过指针调用时也是如此。

package main

import (
     "log"
)

func main() {
     u1 := user{"Tom"}
     u2 := &user{"Jerry"}

     u1.Name()
     u2.Name()

     log.Printf("%p %p",&u1,u2)
}

type user struct {
     name string
}

func (r user) Name() {
     log.Printf("%s %p %p",r.name,&r,&r.name)
}

如果函数需要修改接收者的状态,要以指针作为接收者。否则使用值作为接收者。

Go中的引用类型有:切片、映射、通道、接口和函数。

接口是用于定义行为的类型。如果一个类型实现了某个接口所声明的全部方法,这个类型的对象就可以赋值给做对应接口类型的变量。在赋值完成后, 会建立一个接口对象。接口对象包含两个指针:一个指向iTable,一个指向存储的值。iTable包含了存储值的类型信息,以及与这个值相关联的一组方法,称为方法集。方法集定义了接口的接收规则。

方法接收者
T (t T)
*T (t T) 和 (t*T)

嵌入类型:

type user struct {
  name string
  email string
}

func (r user) hello() string {
  return "hello " + r.name
}

type admin struct {
  user
  level string
}

a := admin{}
a.user.name
a.name
a.user.hello()
a.hello()

被嵌入的类型也叫内部类型。内部类型中的标志符(成员和函数)会被提升到外部类型中。

以小写字母开头的标识符是包私有标识符,在包外不可见。对于未公开的内部类型,其公开成员可以通过标识符提升,以外部类型成员的方式访问。

第六章

Go使用的同步模型是通信顺序模型(Communicating Sequential Processes,CSP),各个goroutine通过传递消息通信,而非通过锁和共享内存来共享状态。

Go运行时会在逻辑处理器上调度goroutine。从1.5版本起,Go运行时会为每个物理处理器分配一个逻辑处理器(每个CPU一个还是每个核一个?)。当goroutine指定到一个阻塞的系统调用时,运行时将线程和goroutine从逻辑处理器上分离。被分离的线程会继续阻塞,等待系统调用返回。而逻辑处理器会建立一个新线程,从队列中选取一个goroutine,将新线程和goroutine绑定到逻辑处理器上。

在处理网络I/O时,goroutine会集成到网络轮询器的运行时。

Go运行时的线程数量默认为10000。超过这个数量时,运行时会崩溃。使用runtime/debug包中的方法SetMaxThreads()可以提高线程数量。

并发concurrency不是并行parallelism。并行是让不同的代码片段同时在不同的物理处理器上执行。并行指同时处理很多事情。并发指同时管理很多事情。

调用runtime包的方法GOMAXPROCS()可以设置Go运行时逻辑处理器数量。

next:
  for i := 0; i < 10; i++ {
    for j := 0; j < 10; j++ {
      if cond {
        continue next
      }
    }
  }

runtime.NumCPU()返回物理处理器数量。

runtime.Gosched()从线程退出,并放回队列。

unbuffered := make(chan int) // 无缓冲区的通道
buffered := make(chan int,10) // 有缓冲区的通道

无缓冲区通道要求发送方和接收方同时准备好。如果一方没有准备好,另一方会阻塞。

package main

import (
     "fmt"
)

func main() {
     input := make(chan int)
     go func() {
             input <- 1
     }()
     foo(input,10)
}

func foo(input chan int,end int) {
     x := <-input
     fmt.Println(x)
     if x >= end {
             return
     }
     go foo(input,end)
     input <- x + 1
}

第七章

import (
  "os"
  "os/signal"
)

signalQueue := make(chan os.Signal)
signal.Notify(signalQueue,os.Interrupt) # 接收信号
for {
  if interrupted() {
    break
  }
  // ...
}

func interrupted() bool {
   select {
   case <-signalQueue:
     signal.Stop(signalQueue) # 停止接收信号
     return true
   default:
     return false
   }
}
a := []int{1,2}
func add(arr ...int) {
  b := append(a,arr...)
}

判断超时和终端的示例:

interrupt := make(chan os.Signal,1)
complete := make(chan error)
timeout := time.After(3 * time.Second)

signal.Notify(r.interrupt,os.Interrupt)
go func() {
  complete <- someFunc()
}()

select {
case err := <-complete:
  return err
case <-r.timeout:
  return "timeout"
}

每个调用signal.Notify(signalChan,signum)的队列,都会收到信号。

第八章

import "log"

log.SetPrefix("TRACE: ")
log.SetFlags(log.Ldata | log.Llongfile)
// Ldate Ltime Llongfile Lmicroseconds Lshortfile
// LstdFlags = Ldate | Ltime
log.Println("abc")
log.Fatalln("abc")
log.Panicln("abc")

log.New(ioutil.Discard,"TRACE: ",log.LstdFlags|log.Lshortfile)
log.New(io.MultiWriter(file,os.Stderr),"ERROR: ",log.LstdFlags|log.Lshortfile)

iota关键字为每个常量复制相同的表达式,并且每次自增1:

const (
  a = 1 << iota // 1
  b             // 2
  c             // 4
)

const (
  x = iota // 0
  y        // 1
  z        // 2
)

第九章

单元测试示例:

import "testing"

func TestFoo(t *testing.T) {
  t.Log("abc")
  t.Logf("a = %v",2)
  t.Errorf("%v",123)
  t.Fatal("abc")
}

测试web服务示例:

import (
  "testing"
  "net/http"
  "net/http/httptest"
)

feed := `<xml ...>`

func MockServer() *httptest.Server {
  return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter,r *http.Request) {
    w.WriteHeader(200)
    w.Header().Set("Content-Type","application/xml")
    fmt.Fprintln(w,feed)
  }))
}

func TestFoo(t *testing.T) {
  server := mockServer()
  defer server.Close()

  resp,err := http.Get(server.URL)
  if err != nil {
    t.Fatal(err)
  }
  defer resp.Body.Close()
  // ...
}

测试web服务示例:

http.HandleFunc("/some",func(rw http.ResponseWriter,r *http.Request) {
  // ...
})

func TestSome(t *testing.T) {
  req,_ := http.NewRequest("GET","/some",nil)
  rw := httptest.NewRecorder()
  http.DefaultServeMux.ServeHTTP(rw,req)
}

基准测试:

func BenchmarkSprintf(b *testing.B) {
  number := 10
  b.ResetTimer()
  for i := 0; i < b.N; i++ {
    fmt.Sprintf("%d",number)
  }
}

修订记录

  1. 2017年08月02日 建立文档。
  2. 2017年08月05日 增加笔记。
  3. 2017年08月07日 修正拼写错误。

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读