国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

Go語(yǔ)言入門記錄:從基礎(chǔ)到變量、函數(shù)、控制語(yǔ)句、包引用、interface、panic、go協(xié)程、Channel、sync下的waitGroup和Once等

這篇具有很好參考價(jià)值的文章主要介紹了Go語(yǔ)言入門記錄:從基礎(chǔ)到變量、函數(shù)、控制語(yǔ)句、包引用、interface、panic、go協(xié)程、Channel、sync下的waitGroup和Once等。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問(wèn)。

  1. 程序入口文件的包名必須是main,但主程序文件所在文件夾名稱不必須是main,即我們下圖hello_world.gomain中,所以感覺(jué)package main寫(xiě)順理成章,但是如果我們把main目錄名稱改成隨便的名字如filename也是可以運(yùn)行的,所以迷思就在于寫(xiě)在文件開(kāi)頭的那個(gè)package mainjava中不是一個(gè)概念。主程序中函數(shù)是固定的。運(yùn)行這個(gè)文件用go run hello_world.go,使用go build hello_world.go會(huì)在同目錄下生成一個(gè)執(zhí)行文件,在windows上就是一個(gè)同名的exe文件,如hello_world.exe,使用.\hello_world.exe也可以執(zhí)行得到結(jié)果。
    Go語(yǔ)言入門記錄:從基礎(chǔ)到變量、函數(shù)、控制語(yǔ)句、包引用、interface、panic、go協(xié)程、Channel、sync下的waitGroup和Once等,golang,開(kāi)發(fā)語(yǔ)言,后端
    Go語(yǔ)言入門記錄:從基礎(chǔ)到變量、函數(shù)、控制語(yǔ)句、包引用、interface、panic、go協(xié)程、Channel、sync下的waitGroup和Once等,golang,開(kāi)發(fā)語(yǔ)言,后端
  2. main函數(shù)沒(méi)有參數(shù),但它可直接通過(guò)os.Args獲取,os.Args是個(gè)數(shù)組,如以下代碼:
package main

import (
	"fmt"
	"os"
)

func main() {
	fmt.Println(os.Args)
	if len(os.Args) > 1 {
		fmt.Println("Hello Go...", os.Args[1])
	}
	os.Exit(0)
}
  1. 快速測(cè)試的方法,單元測(cè)試。文件名用_test.go結(jié)尾,方法用TestXXX開(kāi)頭。包名和其他不論。在VS Code中左邊會(huì)有一個(gè)按鈕,點(diǎn)擊即可測(cè)試,在其他編輯器中,可能點(diǎn)擊保存就會(huì)運(yùn)行。
    Go語(yǔ)言入門記錄:從基礎(chǔ)到變量、函數(shù)、控制語(yǔ)句、包引用、interface、panic、go協(xié)程、Channel、sync下的waitGroup和Once等,golang,開(kāi)發(fā)語(yǔ)言,后端
  2. 以下是變量使用方法。go支持變量類型推斷,即可以省略類型定義。
func TestFib(t *testing.T) {
	var a int
	a = 1
	var b int = 1

	// var (
	// 	a int
	// 	b int = 1
	// )

	t.Log(a)
	for i := 1; i < 10; i++ {
		t.Log(b)
		tmp := a
		a = b
		b = tmp + a
	}
}

以下這個(gè)一句語(yǔ)句對(duì)多個(gè)變量賦值也是常用交換功能了:

func TestSwap(t *testing.T) {
	a := 1
	b := 2
	a, b = b, a
	t.Log(a, b)
}
  1. 常量使用。注意iota是個(gè)語(yǔ)法糖,出現(xiàn)時(shí)會(huì)被設(shè)置位0,代碼運(yùn)行到下一行時(shí)iota自動(dòng)加1,所以以下用到它的代碼定義的常量是左移操作,每次乘以2。
const (
	Monday  = 1
	Tuesday = 2
)

const (
	Friday = 1 << iota
	Saturday
	Sunday
)

const ttt int = 1

func TestWeekDay(t *testing.T) {
	t.Log(ttt)
	t.Log(Monday, Tuesday)
	t.Log(Friday, Saturday, Sunday)
	a := 7 // 0111
	t.Log(a&Friday == Friday, a&Saturday == Saturday, a&Sunday == Sunday)
}
  1. 基本數(shù)據(jù)類型后面會(huì)添加位數(shù),C#和其他語(yǔ)言中也會(huì)有部分影子。注意的是go不支持?jǐn)?shù)據(jù)類型隱式轉(zhuǎn)換。
bool
string
uint8 uint16 uint32 uint64 int8 int16 int32 int64
float32、float64
complex64 complex128
byte 類似 uint8
rune 類似 int32
uint3264uintptr:無(wú)符號(hào)整型,用于存放一個(gè)指針

// 非要轉(zhuǎn)換的話,就顯示轉(zhuǎn)換
b = int64(a)

go語(yǔ)言的指針不支持運(yùn)算

func TestPointer(t *testing.T) {
	a := 1
	aPtr := &a
	// aPtr = aPtr + 1 // 不支持指針運(yùn)算
	t.Log(a, aPtr) //1 0xc000018320
	t.Logf("%T %T", a, aPtr) //int *int
}

string默認(rèn)是空字符串,而不是nullnil。

func TestString(t *testing.T) {
	var s string
	t.Log("*" + s + "*") //**
	t.Log(s == "")       //true
}
  1. go中沒(méi)有前置的自增和自減,之后后置的a++a--。go中數(shù)組的比較不是比較引用地址,而是直接比較數(shù)組長(zhǎng)度和值,只有數(shù)組長(zhǎng)度相同才能比較,不然直接編譯報(bào)錯(cuò),其次每個(gè)元素都相等,數(shù)組才相等。
func TestArray(t *testing.T) {
	a := [...]int{1, 2, 3, 4}
	//b := [...]int{1, 2, 3, 4, 5}
	c := [...]int{1, 2, 3, 4}
	d := [...]int{1, 2, 3, 5}

	//t.Log(a == b) // 直接編譯報(bào)錯(cuò)
	t.Log(a == c) //true
	t.Log(a == d) //false
}

有個(gè)按位清零的運(yùn)算符&^,即當(dāng)右邊運(yùn)算數(shù)的位為1時(shí)結(jié)果直接清零,如果是0則左邊運(yùn)算數(shù)的位是什么結(jié)果就是什么。相當(dāng)于右邊操作數(shù)對(duì)左邊操作數(shù)定向位清零。

a := 7 // 0111
Readable := 1 << 0 // 00000001
a = a &^ Readable // 結(jié)果是0110賦值給a
  1. 控制語(yǔ)句。go語(yǔ)言關(guān)鍵字很少大概20多個(gè),這體現(xiàn)在它的循環(huán)只有for循環(huán)一種。
n := 0
for n < 5 {
	n++
	...
}

// 無(wú)限循環(huán)
for {
	...
}

// 條件語(yǔ)句
if a == b {
} else if c == d {
} else {
}
// 比較特殊的是if后面可以有賦值和條件,如前面定義個(gè)a后面條件就用這個(gè)a
if res,err := someFunc(), err == nil {
	// 如果錯(cuò)誤為空,即返回正確則...
} else {
}

// switch不局限于常量或整數(shù),還可以充當(dāng)if的角色,case后面可以寫(xiě)多個(gè)匹配項(xiàng),以省略break。
switch i {
	case 0,2: // 可以多個(gè)項(xiàng)
		...   // 可以省略break
	case 1:
		...
	default:
		...
}
switch {
	case i > 1 && i < 3: // 類似if判斷語(yǔ)句
		...
	default:
		....
}
  1. 數(shù)組相關(guān)。
func TestArrayLearning(t *testing.T) {
	var a [3]int
	a[0] = 1
	t.Log(a) // [1 0 0]

	arr1 := [4]int{1, 2, 3}
	t.Log(arr1) // [1 2 3 0] 沒(méi)賦值的初始化為0

	arr2 := [...]int{1, 3, 5, 7}
	t.Log(arr2) //[...]表示數(shù)組長(zhǎng)度會(huì)按照后面的值得數(shù)量初始化

	for i := 0; i < len(arr2); i++ {
		t.Log(arr2[i])
	}
	// 快捷方法
	for idx, v := range arr2 {
		t.Log(idx, v)
	}
	// 如果不要索引,則用下劃線占位,不能直接去掉,不然報(bào)錯(cuò)
	for _, v := range arr2 {
		t.Log(v)
	}
	// 包含開(kāi)始,不包含結(jié)束
	t.Log(arr2[1:3]) //[3 5]
	t.Log(arr2[1:])  //[3 5 7]
	t.Log(arr2[:3])  //[1 3 5]
}
  1. 切片相關(guān)。
func TestSlice(t *testing.T) {
	var s0 []int
	t.Log(len(s0), cap(s0)) //0
	s0 = append(s0, 1)
	t.Log(len(s0), cap(s0)) //1 1

	s1 := []int{1, 2, 3, 4}
	t.Log(len(s1), cap(s1)) //4 4

	s2 := make([]int, 3, 5)
	//t.Log(s2[0], s2[1], s2[2], s2[3])// 會(huì)報(bào)錯(cuò),因?yàn)殡m然初始化容量是5,但是只有3個(gè)元素可訪問(wèn)
	s2 = append(s2, 1)
	t.Log(s2[0], s2[1], s2[2], s2[3]) //0 0 0 1
}

切片其實(shí)是共享了存儲(chǔ)空間,即如果兩個(gè)切片有重疊元素,那么修改時(shí)會(huì)相互影響。
Go語(yǔ)言入門記錄:從基礎(chǔ)到變量、函數(shù)、控制語(yǔ)句、包引用、interface、panic、go協(xié)程、Channel、sync下的waitGroup和Once等,golang,開(kāi)發(fā)語(yǔ)言,后端

func TestSliceShare(t *testing.T) {
	var s = []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
	s1 := s[3:6]
	s2 := s[5:8]
	t.Log(s1, len(s1), cap(s1)) // [4 5 6] 3 6
	t.Log(s2, len(s2), cap(s2)) // [6 7 8] 3 4
	s2[0] = 100
	t.Log(s1) // [4 5 100]
	//t.Log(s1 == s2) // 切片不能互相比較,和數(shù)組不同
	t.Log(s1 == nil)  // 切片只能和nil比較 
}
  1. map,主要注意的是判斷key是否存在。遍歷也是用range得到k,v。
func TestMap(t *testing.T) {
	m1 := map[int]int{1: 100, 2: 200, 3: 300}
	t.Log(m1[1]) //100

	m2 := map[string]string{"name": "eric", "gender": "male"}
	t.Logf("len m2 = %d", len(m2)) //len m2 = 2

	m3 := map[string]int{}
	m3["math"] = 59
	m3["language"] = 61
	t.Logf("len m3 = %d", len(m3)) //len m3 = 2

	m4 := make(map[int]int, 10)    // 10是capacity
	t.Logf("len m4 = %d", len(m4)) //len m4 = 0

	// 如果key不存在則根據(jù)value類型反饋對(duì)應(yīng)的初始化的值,如int的0或string的空字符串
	t.Log(m1[4])     // 0
	t.Log(m2["age"]) // 空字符串
	// 所以無(wú)法判斷原先就是0值和空字符串,go提供了判斷方法
	if v, ok := m1[4]; ok {
		t.Log("value is %i", v)
	} else {
		t.Log("key is not existing")
	}

	// 遍歷map,也是range
	for k, v := range m2 {
		t.Logf("%s : %s", k, v)
	}
}

比較牛的是map的value還可以是函數(shù):

m8 := map[int]func(num int) int{}
m8[1] = func(num int) int { return num }
m8[2] = func(num int) int { return num * num }
m8[3] = func(num int) int { return num * num * num }
t.Log(m8[1](2)) //2
t.Log(m8[2](2)) //4
t.Log(m8[3](2)) //8

go集合沒(méi)有set,可以用map[type]bool來(lái)變相實(shí)現(xiàn)。

m9 := map[int]bool{}
m9[1] = true
t.Log(m9[1] == true) // true
delete(m9, 1)
t.Log(m9[1] == true) // false
  1. 字符串。
func TestString1(t *testing.T) {
	var s1 string
	s1 = "hello"
	t.Log(len(s1)) // 5

	//s1[1] = "k" // 字符串其實(shí)是不可更改的切片

	s2 := "\x53\xbb"
	t.Log(s2)      // 是個(gè)亂碼
	t.Log(len(s2)) // 2,不管實(shí)際寫(xiě)的是什么,長(zhǎng)度最終看看byte的長(zhǎng)度

	s3 := "中"
	t.Log(len(s3))
	c := []rune(s3)            //3
	t.Logf("Unicode:%x", c[0]) //Unicode:4e2d
	t.Logf("UTF8:%x", s3)      //UTF8:e4b8ad

	s4 := "\xe4\xb8\xad"
	t.Log(len(s4)) //3
	t.Log(s4)      //中

	// range返回的是rune,但是我們可以通過(guò)格式化輸出進(jìn)行轉(zhuǎn)換成字符或者二進(jìn)制等形式
	// 中 4e2d 20013
	// 華 534e 21326
	// 民 6c11 27665
	// 族 65cf 26063
	s5 := "中華民族"
	for _, c := range s5 {
		t.Logf("%[1]c %[1]x %[1]d", c)
	}
}

兩個(gè)庫(kù)用戶擴(kuò)展字符串操作的:

import (
	"strings"
	"strconv"
)
func TestOperateString(t *testing.T) {
	s1 := "1,2,3,4"
	parts := strings.Split(s1, ",")
	for _, part := range parts {
		t.Log(part)
	}
	t.Log(strings.Join(parts, "-")) //1-2-3-4

	t.Logf("string is %s", strconv.Itoa(100)) //string is 100
	if v, err := strconv.Atoi("100"); err == nil {
		t.Logf("result is %d", 100+v) //result is 200
	}
}
  1. 函數(shù),一等公民。
  • 可返回多個(gè)值
  • 所有參數(shù)都是值傳遞
  • 函數(shù)可以作為變量的值,作為參數(shù)值,可以作為返回值
  • 如果多個(gè)返回值,不需要多個(gè)時(shí),用下劃線代替,之前有演示過(guò)
func ComputeFunction(op1 int, op2 int) (int, int, int, int) {
	return op1 + op2, op1 - op2, op1 * op2, op1 / op2
}

func TestCompute(t *testing.T) {
	t.Log(ComputeFunction(3, 5)) //8 -2 15 0
}

再看看函數(shù)作為參數(shù)和返回值的情況:

func timeSpent(innerFunc func(op int) int) func(op int) int {
	return func(n int) int {
		start := time.Now()
		ret := innerFunc(n)
		fmt.Println("time spent seconds:", time.Since(start).Seconds())
		return ret
	}
}

func slowFunc(n int) int {
	time.Sleep(time.Second * 2)
	return n
}

func TestFunc(t *testing.T) {
	resFunc := timeSpent(slowFunc) // 得到了一個(gè)給slowFunc增強(qiáng)的函數(shù),即增加了打印計(jì)時(shí)功能
	t.Log(resFunc(100))            // 100,得到的函數(shù)可以直接使用
}

再來(lái)看看可變參數(shù),這個(gè)其他語(yǔ)言也有:

func sum(ops ...int) int {
	s := 0
	for _, v := range ops {
		s += v
	}
	return s
}

func TestSum(t *testing.T) {
	t.Log(sum(1, 2, 3, 4, 5)) //15
	t.Log(sum(1, 2, 3))       //6
}

再看看延遲執(zhí)行:

func ClearResource() {
	fmt.Println("Clear resource...")//2.不管有無(wú)報(bào)錯(cuò),最后都會(huì)先輸出Clear resource...
}

func TestDeferFunc(t *testing.T) {
	defer ClearResource() // defer相當(dāng)于try-catch中的finally,函數(shù)最后會(huì)執(zhí)行它,盡管有panic報(bào)錯(cuò)也會(huì)執(zhí)行它
	fmt.Println("Start") // 1.先輸出Start
	panic("error")
}

最后補(bǔ)充自定義類型type,可以簡(jiǎn)化代碼:

// 自定義一個(gè)類型,下面就能用這個(gè),相當(dāng)于定一個(gè)類型別名
type convFunc func(op int) int
func timeSpent(innerFunc convFunc ) convFunc  {
	return func(n int) int {
		start := time.Now()
		ret := innerFunc(n)
		fmt.Println("time spent seconds:", time.Since(start).Seconds())
		return ret
	}
}
  1. go語(yǔ)言不支持繼承(黑魔法寫(xiě)法除外),建議使用復(fù)合。go語(yǔ)言中定義結(jié)構(gòu)和行為如下:
type User struct {
	Id   string
	Name string
	Age  int
}

func TestStruct(t *testing.T) {
	e1 := User{"1", "Eric", 18}
	t.Log(e1) //{1 Eric 18}
	e2 := User{Id: "2", Name: "Tom"}
	t.Log(e2.Name)  //Tom
	e3 := new(User) // 這種其實(shí)返回的是一個(gè)引用/指針
	e3.Id = "3"
	e3.Name = "Jerry"
	t.Log(e3.Age) //0

	t.Logf("%T", e1) //test.User
	t.Logf("%T", e3) //*test.User
}

定義行為和普通函數(shù)不太一樣,需要注意一下,建議使用引用方式,即結(jié)構(gòu)體參數(shù)使用user *User之類的格式:

unc (user User) StringFmt1() string {
	fmt.Printf("StringFmt1:The Name Address is %x\n", unsafe.Pointer(&user.Name))
	return fmt.Sprintf("%s-%s-%d", user.Id, user.Name, user.Age)
}

// 建議使用這種引用方法,避免復(fù)制產(chǎn)生的消耗
func (user *User) StringFmt2() string {
	fmt.Printf("StringFmt2: The Name Address is %x\n", unsafe.Pointer(&user.Name))
	return fmt.Sprintf("%s/%s/%d", user.Id, user.Name, user.Age)
}

// 這是普通函數(shù)
func StringFmt3(user User) string {
	fmt.Printf("StringFmt3: The Name Address is %x\n", unsafe.Pointer(&user.Name))
	return fmt.Sprintf("%s/%s/%d", user.Id, user.Name, user.Age)
}

func TestStructFunc(t *testing.T) {
	u := User{Id: "100", Name: "Robin", Age: 99}
	fmt.Printf("The Name Address is %x\n", unsafe.Pointer(&u.Name)) //The Name Address is c000114910
	t.Log(u.StringFmt1())                                           //StringFmt1:The Name Address is c000114940
	t.Log(u.StringFmt2())                                           //StringFmt2: The Name Address is c000114910
	t.Log(StringFmt3(u))                                            //StringFmt3: The Name Address is c0001149a0
}
  1. go的接口比較解耦,不會(huì)強(qiáng)相互依賴,這可以從下面的例子看出來(lái)。下面的例子先寫(xiě)了一個(gè)普通的結(jié)構(gòu)體并定義了一個(gè)行為,這時(shí)候沒(méi)有任何接口。如果我們?cè)敢獾脑?,可以把這個(gè)行為單獨(dú)提取出來(lái)形成一個(gè)接口。這個(gè)過(guò)程我們發(fā)現(xiàn),提取接口完全不需要修改原先行為的代碼和使用。只需要方法簽名一致就行。
type Employee struct {
	Age int
}

func (e *Employee) AgeAddOneYear() int {
	return e.Age + 1
}

func TestInterface(t *testing.T) {
	e := Employee{Age: 18}
	t.Log(e.AgeAddOneYear()) // 19
}

然后在文件任何位置提取一個(gè)接口,保證簽名一致即可。原先代碼正常運(yùn)行。我們稱這種接口為沒(méi)有侵入性。我們的實(shí)現(xiàn)也不依賴這個(gè)接口定義,即沒(méi)有這個(gè)接口的時(shí)候我們也能正常運(yùn)行使用。

type MyInterface interface {
	AgeAddOneYear() int
}
  1. 復(fù)合。
  • go有沒(méi)有繼承,只要使用父類去定義子類即可發(fā)現(xiàn)var pet Pet := new(Dog)是不可以的,連強(qiáng)制轉(zhuǎn)換也是不行的。
  • 所以一個(gè)變量在初始化的時(shí)候就決定了它的類型是Pet還是Dog,那么它接下來(lái)的行為就好說(shuō)了:以為Dog復(fù)合了Pet的數(shù)據(jù)和行為,如果Dog自己有就調(diào)用自己的,自己沒(méi)有就調(diào)用Pet的。所以下面的func (d *Dog) SpeakTo(host string)是關(guān)鍵。
type Pet struct{}

type Dog struct {
	Pet
}

func (p *Pet) Speak() {
	fmt.Printf("Pet is speaking")
}

func (p *Pet) SpeakTo(host string) {
	p.Speak()
	fmt.Print(":", host)
}

func (d *Dog) Speak() {
	fmt.Printf("Dog is speaking")
}

// 如果沒(méi)有這個(gè)行為,則輸出的是Pet的行為,因?yàn)閺?fù)合了Pet的代碼:Pet is speaking:Eric
// 如果有這個(gè)行為,則輸出的是Dog的行為:Dog is speaking:Eric
func (d *Dog) SpeakTo(host string) {
	d.Speak()
	fmt.Print(":", host)
}

func TestInherit(t *testing.T) {
	d := new(Dog)
	d.SpeakTo("Eric")
}
  1. 多態(tài)的實(shí)現(xiàn),主要在于那個(gè)方法的參數(shù)使用接口參數(shù)。
type Duck struct{}
type Goose struct{}

func (d *Duck) bark() string {
	return "gua gua gua..."
}

func (g *Goose) bark() string {
	return "e e e..."
}

// 這個(gè)方法是關(guān)鍵,使用了接口作為參數(shù),這樣就能實(shí)現(xiàn)多態(tài)
func diffrentbark(interf MyInterface2) string {
	return interf.bark()
}

func TestBark(t *testing.T) {
	duck := new(Duck)
	t.Log(diffrentbark(duck)) //gua gua gua...
	goose := new(Goose)
	t.Log(diffrentbark(goose)) //e e e...
}
  1. 接口的常見(jiàn)使用方法。
  • 按照上述的多態(tài)的情況,定義函數(shù)的時(shí)候參數(shù)我們經(jīng)常用接口,但更常用的是我們使用空接口
  • 配合斷言判斷不同情況
  • 好的習(xí)慣是讓接口盡量小,一般包含一個(gè)方法,如果需要大的接口則使用小接口組合而成
// 注意這里的參數(shù),是一個(gè)空接口,然后在代碼里判斷這個(gè)接口實(shí)際傳過(guò)來(lái)的可能的值或者類型,做相應(yīng)的行為
func UseEmptyInterface(p interface{}) {
	// if v, ok := p.(int); ok {
	// 	fmt.Println("int is", v)
	// } else if v, ok := p.(string); ok {
	// 	fmt.Println("string is", v)
	// }
	switch v := p.(type) {
	case int:
		fmt.Println("int is", v)
	case string:
		fmt.Println("string is", v)
	default:
		fmt.Println("unknown")
	}
}

func TestEmptyInterface(t *testing.T) {
	UseEmptyInterface(100)   //int is 100
	UseEmptyInterface("100") //string is 100
}
  1. 錯(cuò)誤的處理方法,正常情況下使用errors.New("")即可。如果錯(cuò)誤類型比較多,可以定義錯(cuò)誤類型var xxx = errors.New()。這里需要注意的是,要習(xí)慣于go這種返回多個(gè)值得形式,并利用多個(gè)參數(shù)這個(gè)將返回值和提示信息一并返回。
var LessThanTwo = errors.New("The num should not less that 2.")

func GetFib(n int) ([]int, error) {
	if n < 2 {
		return nil, LessThanTwo
	}
	if n > 100 {
		return nil, errors.New("The num should not more than 100.")
	}
	fibList := []int{1, 1}
	for i := 2; i < n; i++ {
		fibList = append(fibList, fibList[i-2], fibList[i-1])
	}
	return fibList, nil
}

func TestError(t *testing.T) {
	if lst, err := GetFib(2); err != nil {
		t.Log(err)
	} else {
		t.Log(lst)
	}
}

看一下recover的使用方法,配合defer使用,可以獲得錯(cuò)誤信息,在這里釋放資源或者做一些其他操作:

func TestRecover(t *testing.T) {
	defer func() {
		if err := recover(); err != nil {
			t.Log("recover from error:", err) //recover from error: crashed...
		}
	}()
	t.Log("start...")
	panic(errors.New("crashed..."))
}

需要注意的是如果使用os.Exist(0)退出的話,是不會(huì)執(zhí)行defer的。

  1. 包的引用方式不同版本的go不一樣,早期的go項(xiàng)目通過(guò)設(shè)置GOPATH,所有項(xiàng)目共用一套GOOATH以及包版本,所有的代碼寫(xiě)在src文件夾中。后來(lái)有go mod的模塊管理工具,每個(gè)項(xiàng)目都可以寫(xiě)在不同位置,只要先使用go mod init projectName初始化項(xiàng)目即可。后面就可以互相使用本地包,從項(xiàng)目名開(kāi)始寫(xiě),比如下面初始化了一個(gè)goProjects項(xiàng)目。
    Go語(yǔ)言入門記錄:從基礎(chǔ)到變量、函數(shù)、控制語(yǔ)句、包引用、interface、panic、go協(xié)程、Channel、sync下的waitGroup和Once等,golang,開(kāi)發(fā)語(yǔ)言,后端
    第三方包的引用和使用:
  • 先下載
  • 后引用
// 去官網(wǎng)查詢包https://pkg.go.dev
// go get下載 -u強(qiáng)制更新
go get -u github.com/google/go-cmp/cmp

// 在項(xiàng)目中使用
import (
	"testing"
	cmp "github.com/google/go-cmp/cmp"
)

func TestImportThirdPackage(t *testing.T) {
	t.Log(cmp.Equal(1, 2)) // false
}

這個(gè)時(shí)候項(xiàng)目中有2個(gè)文件:

  • go.mod,里面記錄了依賴版本的全部信息
  • go.sum,記錄了所有依賴module的校驗(yàn)信息

注意一些歷史小知識(shí):最早的沒(méi)有模塊管理和包管理,后來(lái)有了vendor+利用第三方glide或dep來(lái)管理,它們會(huì)生成一個(gè)配置文件,在這個(gè)配置文件里記錄了項(xiàng)目使用的依賴包信息。再后來(lái)官方退出了module功能。這就是簡(jiǎn)單的go項(xiàng)目管理和依賴管理的發(fā)展。

  1. 協(xié)程。關(guān)于goroutine協(xié)程們可以參考這邊文章GMP 原理與調(diào)度。原先只有線程M和協(xié)程G,雖然實(shí)現(xiàn)了M對(duì)N的關(guān)系。但是仍然存在問(wèn)題,后來(lái)增加了處理器P即調(diào)度器。

在 Go 中,線程是運(yùn)行 goroutine 的實(shí)體,調(diào)度器的功能是把可運(yùn)行的 goroutine 分配到工作線程上。
1、全局隊(duì)列(Global Queue):存放等待運(yùn)行的 G。
2、P 的本地隊(duì)列:同全局隊(duì)列類似,存放的也是等待運(yùn)行的 G,存的數(shù)量有限,不超過(guò) 256 個(gè)。新建 G’時(shí),G’優(yōu)先加入到 P 的本> 地隊(duì)列,如果隊(duì)列滿了,則會(huì)把本地隊(duì)列中一半的 G 移動(dòng)到全局隊(duì)列。
3、P 列表:所有的 P 都在程序啟動(dòng)時(shí)創(chuàng)建,并保存在數(shù)組中,最多有 GOMAXPROCS(可配置) 個(gè)。
4、M:線程想運(yùn)行任務(wù)就得獲取 P,從 P 的本地隊(duì)列獲取 G,P 隊(duì)列為空時(shí),M 也會(huì)嘗試從全局隊(duì)列拿一批 G 放到 P 的本地隊(duì)> 列,或從其他 P 的本地隊(duì)列偷一半放到自己 P 的本地隊(duì)列。M 運(yùn)行 G,G 執(zhí)行之后,M 會(huì)從 P 獲取下一個(gè) G,不斷重復(fù)下去。

Go語(yǔ)言入門記錄:從基礎(chǔ)到變量、函數(shù)、控制語(yǔ)句、包引用、interface、panic、go協(xié)程、Channel、sync下的waitGroup和Once等,golang,開(kāi)發(fā)語(yǔ)言,后端
以下的協(xié)程代碼,在函數(shù)前加go就進(jìn)入?yún)f(xié)程了。

func TestRoutine(t *testing.T) {
	for i := 0; i < 10; i++ {
		go func(num int) {
			fmt.Println(num)
		}(i)
	}
	time.Sleep(time.Second * 1)
}
  1. 緊接著就是不同協(xié)程之間的數(shù)據(jù)安全,即鎖。還有waitgroup。
// 沒(méi)有鎖
func TestNotLock(t *testing.T) {
	counter := 0
	for i := 0; i < 5000; i++ {
		go func() {
			counter++
		}()
	}
	time.Sleep(time.Second * 2)
	fmt.Println(counter)
}
// 有鎖,注意defer中釋放
func TestLock(t *testing.T) {
	var mut sync.Mutex
	counter := 0
	for i := 0; i < 5000; i++ {
		go func() {
			defer func() {
				mut.Unlock()
			}()
			mut.Lock()
			counter++
		}()
	}
	time.Sleep(time.Second * 2)
	fmt.Println(counter)
}

waitgroup使用wait的時(shí)候就是等所有的都執(zhí)行完,所有指的多少個(gè),就是add的數(shù)量。使用done相當(dāng)于減1。

func TestLockWaitGroup(t *testing.T) {
	var mut sync.Mutex
	var wg sync.WaitGroup
	counter := 0
	for i := 0; i < 5000; i++ {
		wg.Add(1)
		go func() {
			defer func() {
				mut.Unlock()
				wg.Done()
			}()
			mut.Lock()
			counter++
		}()
	}
	wg.Wait()
	fmt.Println(counter)
}
  1. csp并發(fā)機(jī)制:channel。分為兩種一種是一對(duì)一交換數(shù)據(jù),只有雙方發(fā)送和接收都完成了才能往下進(jìn)行否則阻塞,即這個(gè)指需要從channel中拿走才行。第二種是給channel加buffer,即發(fā)送和接收解耦,不會(huì)阻塞。注意channel的符號(hào)<-往channel中放數(shù)據(jù)和取數(shù)據(jù)。
func doTask() chan string {
	// 如果使用這種一對(duì)一的channel,那么只有當(dāng)fmt.Println(<-retCh)執(zhí)行時(shí)相當(dāng)于接收數(shù)據(jù)后,這個(gè)retCh <- "init data..."才取消阻塞繼續(xù)執(zhí)行
	// 所以fmt.Println("doing task done")總是在fmt.Println(<-retCh)后執(zhí)行
	//retCh := make(chan string)
	
	// 如果使用這種channel,1就是buffer,這時(shí)候不用阻塞,retCh <- "init data..."執(zhí)行完直接執(zhí)行fmt.Println("doing task done"),不用管最后fmt.Println(<-retCh)何時(shí)從channel中接收數(shù)據(jù)
	retCh := make(chan string, 1)
	go func() {
		fmt.Println("doing task")
		retCh <- "init data..."
		fmt.Println("doing task done")
	}()
	return retCh
}

func doOtherTask() {
	fmt.Println("doing other task")
	time.Sleep(time.Second * 3)
	fmt.Println("doing other task is done")
}

func TestAsyncChannel(t *testing.T) {
	retCh := doTask()
	doOtherTask()
	fmt.Println(<-retCh)
}

另外有一個(gè)select多路選擇器,了解一下,類似于switch,可以從不同channel中獲取結(jié)果進(jìn)行處理:

func TestSelect(t *testing.T) {
	select {
	case retCh := <-doTask():
		fmt.Print(retCh)
	case <-time.After(time.Second * 1):
		fmt.Print("time out")
	}
}

channel還有一個(gè)close方法。假如有個(gè)生產(chǎn)者和消費(fèi)者,因?yàn)橄M(fèi)者不知道生產(chǎn)者生產(chǎn)數(shù)量,所以我們用無(wú)限for循環(huán),但如果一直沒(méi)有生產(chǎn),那么就會(huì)一直阻塞在消費(fèi)者獲取數(shù)據(jù)處,如果生產(chǎn)結(jié)束時(shí)給一個(gè)關(guān)閉通道的信號(hào),消費(fèi)者判斷是否已經(jīng)傳送結(jié)束,那么就能針對(duì)性處理。以下就是通過(guò)取值時(shí)不僅可以取值還能獲取狀態(tài),如果是true就正常,如果是false意味著關(guān)閉了:文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-678526.html

  • 如果通道已關(guān)閉,無(wú)法傳數(shù)據(jù),會(huì)報(bào)錯(cuò)
  • 如果通道關(guān)閉,則會(huì)喚醒所有的訂閱者,并且傳狀態(tài)為false,可以用于信號(hào)訂閱發(fā)布
    Go語(yǔ)言入門記錄:從基礎(chǔ)到變量、函數(shù)、控制語(yǔ)句、包引用、interface、panic、go協(xié)程、Channel、sync下的waitGroup和Once等,golang,開(kāi)發(fā)語(yǔ)言,后端
    上面說(shuō)的利用這個(gè)close實(shí)現(xiàn)訂閱發(fā)布比如關(guān)閉所有協(xié)程。
// 比如我們這邊判斷要不要關(guān)閉協(xié)程,則可以用
func isCanceled(ch chan struct{}) bool {
	select {
	// 只要收到消息,就會(huì)走case <-ch,當(dāng)然這個(gè)消息我們一般用于關(guān)閉
	case <-ch:
		return true
	default:
		return false
	}
}
// 此時(shí),在程序中使用channel,有需要時(shí)就關(guān)閉這個(gè)channel,在需要用到isCanceled判斷的地方就能判斷已關(guān)閉,然后做相應(yīng)操作
  1. context和取消。如果遇到一個(gè)嵌套的任務(wù),取消一個(gè)節(jié)點(diǎn)時(shí)需要取消其子節(jié)點(diǎn),就可以用context實(shí)現(xiàn)。
func isCanceled(ctx context.Context) bool {
	select {
	case <-ctx.Done():
		return true
	default:
		return false
	}
}

func TestContextCancel(t *testing.T) {
	ctx, cancel := context.WithCancel(context.Background())

	for i := 0; i < 5; i++ {
		go func(i int, ctx context.Context) {
			for {
				if isCanceled(ctx) {
					break
				}
				time.Sleep(time.Millisecond * 10)
			}
			fmt.Println(i, "canceled")
		}(i, ctx)
	}
	cancel()
	time.Sleep(time.Second * 1)
}
// 輸出結(jié)果:
4 canceled
1 canceled
3 canceled
2 canceled
0 canceled
  1. 還是sync下的一個(gè)Once,類似懶漢模式,只運(yùn)行一次的功能。
type Singleton struct{}

var once sync.Once
var singleInstance *Singleton

func GetSingleton() *Singleton {
	// 這里代碼調(diào)用多次,只執(zhí)行一次
	once.Do(func() {
		fmt.Println("create instance ...")
		singleInstance = new(Singleton)
	})
	return singleInstance
}

func TestSyncOnce(t *testing.T) {
	var wg sync.WaitGroup
	for i := 0; i < 5; i++ {
		wg.Add(1)
		go func() {
			obj := GetSingleton()
			fmt.Printf("obj is %x\n", unsafe.Pointer(obj))
			wg.Done()
		}()
	}
	wg.Wait()
}
// 輸出
create instance ...
obj is ec2ba0
obj is ec2ba0
obj is ec2ba0
obj is ec2ba0
obj is ec2ba0

到了這里,關(guān)于Go語(yǔ)言入門記錄:從基礎(chǔ)到變量、函數(shù)、控制語(yǔ)句、包引用、interface、panic、go協(xié)程、Channel、sync下的waitGroup和Once等的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來(lái)自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場(chǎng)。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • 3.你所不知道的go語(yǔ)言控制語(yǔ)句——Leetcode習(xí)題69

    目錄 本篇前瞻 Leetcode習(xí)題9 題目描述 代碼編寫(xiě) 控制結(jié)構(gòu) 順序結(jié)構(gòu)(Sequence) 聲明和賦值 多返回值賦值 運(yùn)算符 算術(shù)運(yùn)算符 位運(yùn)算符 邏輯運(yùn)算 分支結(jié)構(gòu) if 語(yǔ)句 switch 語(yǔ)句 邏輯表達(dá)式 fallthrough 類型推斷 循環(huán)語(yǔ)句 continue break goto Leetcode習(xí)題69 題目描述 題目分析 代碼編寫(xiě) 本篇

    2024年02月12日
    瀏覽(96)
  • Go語(yǔ)言基礎(chǔ)-if語(yǔ)句

    Go語(yǔ)言基礎(chǔ)-if語(yǔ)句

    ? 原文鏈接:? https://www.fearlazy.com/index.php/post/288.html ? 在目前接觸的幾種語(yǔ)言中條件語(yǔ)句都是必不可少的。很難想象沒(méi)有條件語(yǔ)句要怎么寫(xiě)程序。 ? 1.if語(yǔ)句 Go語(yǔ)言的條件語(yǔ)句和C++的很像,使用if。 其格式如下: if 表達(dá)式為true { ? ?執(zhí)行語(yǔ)句 } 和C++的區(qū)別是條件表達(dá)式

    2023年04月08日
    瀏覽(27)
  • 100天精通Golang(基礎(chǔ)入門篇)——第12天:深入解析Go語(yǔ)言中的集合(Map)及常用函數(shù)應(yīng)用

    100天精通Golang(基礎(chǔ)入門篇)——第12天:深入解析Go語(yǔ)言中的集合(Map)及常用函數(shù)應(yīng)用

    ?? 博主 libin9iOak帶您 Go to Golang Language.? ?? 個(gè)人主頁(yè)——libin9iOak的博客?? ?? 《面試題大全》 文章圖文并茂??生動(dòng)形象??簡(jiǎn)單易學(xué)!歡迎大家來(lái)踩踩~?? ?? 《IDEA開(kāi)發(fā)秘籍》學(xué)會(huì)IDEA常用操作,工作效率翻倍~?? ?? 希望本文能夠給您帶來(lái)一定的幫助??文章粗淺,敬請(qǐng)批

    2024年02月12日
    瀏覽(27)
  • Go語(yǔ)言基礎(chǔ)之變量和常量

    標(biāo)識(shí)符 在編程語(yǔ)言中標(biāo)識(shí)符就是程序員定義的具有特殊意義的詞,比如變量名、常量名、函數(shù)名等等。 Go語(yǔ)言中標(biāo)識(shí)符由字母數(shù)字和_(下劃線)組成,并且只能以字母和_開(kāi)頭。 舉幾個(gè)例子:abc, _, _123, a123 是指編程語(yǔ)言中預(yù)先定義好的具有特殊含義的標(biāo)識(shí)符。

    2024年02月12日
    瀏覽(27)
  • go-基礎(chǔ)-3-函數(shù)-記錄

    類似JavaScript 函數(shù)聲明 函數(shù)類型 無(wú)參無(wú)返回值函數(shù) 有一個(gè)參數(shù)的函數(shù) 有兩個(gè)參數(shù)的函數(shù) 有個(gè)返回值的函數(shù) 有多個(gè)返回值的函數(shù) 形式參數(shù)和實(shí)際參數(shù) 形式參數(shù):函數(shù)定義時(shí),用來(lái)接受外部傳入數(shù)據(jù)的參數(shù),就是形式參數(shù) 實(shí)際參數(shù):調(diào)用函數(shù)時(shí),傳給形參的實(shí)際數(shù)據(jù)叫做實(shí)

    2024年02月11日
    瀏覽(18)
  • 【Golang入門教程】Go語(yǔ)言變量的初始化

    【Golang入門教程】Go語(yǔ)言變量的初始化

    強(qiáng)烈推薦 前些天發(fā)現(xiàn)了一個(gè)巨牛的人工智能學(xué)習(xí)網(wǎng)站,通俗易懂,風(fēng)趣幽默,忍不住分享一下給大家。點(diǎn)擊跳轉(zhuǎn)到網(wǎng)站: 人工智能 推薦一個(gè)個(gè)人工作,日常中比較常用的人工智能工具,無(wú)需魔法,忍不住分享一下給大家。點(diǎn)擊跳轉(zhuǎn)到網(wǎng)站: 人工智能工具 引言 在Go語(yǔ)言中,變量

    2024年04月17日
    瀏覽(106)
  • C語(yǔ)言基本語(yǔ)句(變量類型int、 float、 double、 char,函數(shù)scanf、printf、putchar()、getchar() )

    1. int, float, double, char ①整型int(對(duì)應(yīng)%d) ?int a,b; ?scanf(\\\"%d,%d\\\",a,b); printf (\\\"%d\\\",a); printf(\\\"我今天吃了%d個(gè)蘋果,在黑板上寫(xiě)下整數(shù)%d,這很有趣。\\\",a,b); //printf(\\\"……\\\",變量名)中,“……”部分內(nèi)容比較自由,可隨便發(fā)揮,但必須包括%d,幾個(gè)變量名就對(duì)應(yīng)幾個(gè)%d ②單精度型浮點(diǎn)數(shù)

    2024年02月08日
    瀏覽(30)
  • go語(yǔ)言入門-一文帶你掌握go語(yǔ)言函數(shù)

    go語(yǔ)言入門-一文帶你掌握go語(yǔ)言函數(shù)

    本文go語(yǔ)言入門-掌握go語(yǔ)言函數(shù)收錄于《go語(yǔ)言學(xué)習(xí)專欄》專欄,此專欄帶你從零開(kāi)始學(xué)習(xí)go語(yǔ)言。 在每一種編程語(yǔ)言中都有函數(shù)的概念,函數(shù)是基本的代碼快,用于執(zhí)行一個(gè)任務(wù)。 我們之前寫(xiě)的函數(shù)代碼中,都包含一個(gè)main函數(shù): 這個(gè) main 就是一個(gè)函數(shù)的定義,包含了以下幾

    2024年02月03日
    瀏覽(39)
  • 【GO語(yǔ)言基礎(chǔ)】控制流

    【GO語(yǔ)言基礎(chǔ)】控制流

    【Go語(yǔ)言學(xué)習(xí)】ide安裝與配置 【GO語(yǔ)言基礎(chǔ)】前言 【GO語(yǔ)言基礎(chǔ)】變量常量 【GO語(yǔ)言基礎(chǔ)】數(shù)據(jù)類型 【GO語(yǔ)言基礎(chǔ)】控制流 if結(jié)構(gòu) if 語(yǔ)句:用于根據(jù)條件執(zhí)行代碼塊 if else結(jié)構(gòu) else 語(yǔ)句:與 if 語(yǔ)句一起使用,當(dāng) if 的條件不滿足時(shí)執(zhí)行。 1.if str == “” 2.len(str)==0 switch 語(yǔ)句:用

    2024年02月09日
    瀏覽(17)

覺(jué)得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包