go語言學習總結

1、描述javascript

go語言是直接將源碼編譯成二進制機器碼的語言;它支持面向對象、也支持函數式編程;go是強類型語言;Go 編程語言原生支持併發。Go 使用 Go 協程(Goroutine) 和信道(Channel)來處理併發。java

2、基本語法python

一、切片與數組差異:切片是動態化數組,切片是注意二者初始化和函數的區別git

a.初始化差異golang

數組須要指定大小,不指定也會根據初始化的自動推算出大小,不可改變 編程

a:=[...] int {1,2,3} ; a:=[3] int {1,2,3}數組

切片不須要指定大小瀏覽器

a:=[] int {1,2,3}; a:=make([]int,3); a:=make([] int ,3,5)緩存

b.函數傳遞:併發

數組須要明確指定大小,數組是值傳遞,不一樣的數組老是表明不一樣的存儲。

切片不須要明確指定大小,切片是地址傳遞。多個切片若是表示同一個數組的片斷,它們能夠共享數據;所以一個切片和相關數組的其餘切片是共享存儲的。

func changeArray(a [3]int) {//數組
    a[0] = 100
}
func changeSlice(s []int) {//切片
    s[0] = 100
}

func main(){

    a := [...]int{1, 2, 3}
    changeArray(a)
    fmt.Println(a[0]) //值傳遞,輸出結果:1

    var s []int = []int{1, 2, 3, 4}
    fmt.Println(len(s), cap((s)))
    s = append(s, 6, 7, 8)
    fmt.Println(len(s), cap(s))
    changeSlice(s)
    fmt.Println(s[0]) //地址傳遞,輸出結果:100

}

c.切片和垃圾回收

切片的底層指向一個數組,該數組的實際容量可能要大於切片所定義的容量。只有在沒有任何切片指向的時候,底層的數組內層纔會被釋放,這種特性有時會致使程序佔用多餘的內存。

示例 函數 FindDigits 將一個文件加載到內存,而後搜索其中全部的數字並返回一個切片。

var digitRegexp = regexp.MustCompile("[0-9]+")

func FindDigits(filename string) []byte {
    b, _ := ioutil.ReadFile(filename)
    return digitRegexp.Find(b)
}

這段代碼能夠順利運行,但返回的 []byte 指向的底層是整個文件的數據。只要該返回的切片不被釋放,垃圾回收器就不能釋放整個文件所佔用的內存。換句話說,一點點有用的數據卻佔用了整個文件的內存。

想要避免這個問題,能夠經過拷貝咱們須要的部分到一個新的切片中:

func FindDigits(filename string) []byte {
   b, _ := ioutil.ReadFile(filename)
   b = digitRegexp.Find(b)
   c := make([]byte, len(b))
   copy(c, b)
   return c
}

2.數組和Map,數組的輸出按照數組的放置順序,可是Map是無序輸出的,每次遍歷的順序不同;

var m = map[string]int{
        "unix":         0,
        "python":       1,
        "go":           2,
        "javascript":   3,
        "testing":      4,
        "philosophy":   5,
        "startups":     6,
        "productivity": 7,
        "hn":           8,
        "reddit":       9,
        "C++":          10,
    }
    var keys []string
    for k := range m {
        keys = append(keys, k)
    }
    sort.Strings(keys)
    for _, k := range keys {
        fmt.Println("Key:", k, "Value:", m[k])
    }

3.go語言中的指針

指針是一種存儲變量內存地址(Memory Address)的變量。

如上圖所示,變量 b 的值爲 156,而 b 的內存地址爲 0x1040a124。變量 a 存儲了 b 的地址。咱們就稱 a 指向了 b

指針變量的類型爲 *T,該指針指向一個 T 類型的變量。

&b  獲取b的內存地址值;*a 則是獲取a值對應物理地址值的b值:156;

向函數傳遞指針參數

func change(val *int) {  
    *val = 55
}
func main() {  
    a := 58
    fmt.Println("value of a before function call is",a)
    b := &a
    change(b)
    fmt.Println("value of a after function call is", a)
}

4.那麼何時使用指針接收器,何時使用值接收器?

/*
使用值接收器的方法。
*/
func (e Employee) changeName(newName string) {
    e.name = newName
}

/*
使用指針接收器的方法。
*/
func (e *Employee) changeAge(newAge int) {
    e.age = newAge
}

通常來講,指針接收器可使用在:對方法內部的接收器所作的改變應該對調用者可見時。

指針接收器也能夠被使用在以下場景:當拷貝一個結構體的代價過於昂貴時。考慮下一個結構體有不少的字段。在方法內使用這個結構體作爲值接收器須要拷貝整個結構體,這是很昂貴的。在這種狀況下使用指針接收器,結構體不會被拷貝,只會傳遞一個指針到方法內部使用。

在其餘的全部狀況,值接收器均可以被使用。

5.在方法中使用值接收器 與 在函數中使用值參數

這個話題不少Go語言新手都弄不明白。我會盡可能講清楚。

當一個函數有一個值參數,它只能接受一個值參數。

當一個方法有一個值接收器,它能夠接受值接收器和指針接收器。

讓咱們經過一個例子來理解這一點。

package main
import (
    "fmt"
)
type rectangle struct {
    length int
    width  int
}
func area(r rectangle) {
    fmt.Printf("Area Function result: %d\n", (r.length * r.width))
}
func (r rectangle) area() {
    fmt.Printf("Area Method result: %d\n", (r.length * r.width))
}
func main() {
    r := rectangle{
        length: 10,
        width:  5,
    }
    area(r)
    r.area()

    p := &r
    /*
       compilation error, cannot use p (type *rectangle) as type rectangle
       in argument to area
    */
    //area(p)
    p.area()//經過指針調用值接收器
}

6.在方法中使用指針接收器 與 在函數中使用指針參數

和值參數相相似,函數使用指針參數只接受指針,而使用指針接收器的方法可使用值接收器和指針接收器。

package main
import (
    "fmt"
)
type rectangle struct {
    length int
    width  int
}
func perimeter(r *rectangle) {
    fmt.Println("perimeter function output:", 2*(r.length+r.width))

}
func (r *rectangle) perimeter() {
    fmt.Println("perimeter method output:", 2*(r.length+r.width))
}
func main() {
    r := rectangle{
        length: 10,
        width:  5,
    }
    p := &r //pointer to r
    perimeter(p)
    p.perimeter()
    /*
        cannot use r (type rectangle) as type *rectangle in argument to perimeter
    */
    //perimeter(r)
    r.perimeter()//使用值來調用指針接收器
}

7.並行和併發

並行不必定會加快運行速度,由於並行運行的組件之間可能須要相互通訊。在咱們瀏覽器的例子裏,當文件下載完成後,應當對用戶進行提醒,好比彈出一個窗口。因而,在負責下載的組件和負責渲染用戶界面的組件之間,就產生了通訊。在併發系統上,這種通訊開銷很小。但在多核的並行系統上,組件間的通訊開銷就很高了。因此,並行不必定會加快運行速度!

8.線程與協程區別

線程切換從系統層面遠不止 保存和恢復 CPU上下文這麼簡單。操做系統爲了程序運行的高效性每一個線程都有本身緩存Cache等等數據,操做系統還會幫你作這些數據的恢復操做。因此線程的切換很是耗性能。可是協程的切換隻是單純的操做CPU的上下文,因此一秒鐘切換個上百萬次系統都抗的住。可是協程有一個問題,就是系統並不感知,因此操做系統不會幫你作切換。目前的協程框架通常都是設計成 1:N 模式。所謂 1:N 就是一個線程做爲一個容器裏面放置多個協程。那麼誰來適時的切換這些協程?答案是有協程本身主動讓出CPU,也就是每一個協程池裏面有一個調度器,這個調度器是被動調度的。意思就是他不會主動調度。協程的切換很輕。

9.緩衝信道和非緩衝信道

緩衝的channel:保證往緩衝中存數據先於對應的取數據,簡單說就是在取的時候裏面確定有數據,不然就因取不到而阻塞。

非緩衝的channel:保證取數據先於存數據,就是保證存的時候確定有其餘的goroutine在取,不然就因放不進去而阻塞

3、疑問點

 

go系列教程:https://studygolang.com/subject/2

信道接入技術及協議:https://blog.csdn.net/fivedoumi/article/details/52776832

相關文章
相關標籤/搜索