天天看點

go學習筆記day3

文章目錄

    • json
    • goroutine
      • 設計政策
    • channel
      • 無buffer
      • 有buffer
      • 管道關閉
      • select 和 channel

json

package main

import(
	"fmt"
	// "reflect"
	"encoding/json"
)

type User struct{
	Id int	`json:"id"`
	Name string	`json:"name"`
	Age int	`json:"age"`
	Family []string `json:"family"`
}
func main() {
	user := User{1,"ding",18,[]string{"fei","shu"}}

	//結構體轉換為json
	jsonStr,err:=json.Marshal(user)
	if err!=nil{
		fmt.Println("error",err)
		return
	}

	fmt.Printf("jsonStr= %s\n",jsonStr)

	// jsonStr轉為結構體
	userTest :=User{}
	err=json.Unmarshal(jsonStr,&userTest)
	if err!=nil{
		fmt.Println("error",err)
		return
	}
	fmt.Printf("%v\n",userTest)	
}
/*
jsonStr= {"id":1,"name":"ding","age":18,"family":["fei","shu"]}
{1 ding 18 [fei shu]}
*/
           

goroutine

  • g: 代表使用者态線程(協程)使用者自行定義,切換不涉及核心态使用者态切換
  • p: 使用者态線程的排程器,用來排程自定義的協程
  • m: 真正的線程,可以線程可以經過排程器擁有多個協程。多個協程共同占有這個線程的資源

設計政策

  • 複用線程
    • work stealing:當一個排程器p空閑時可将其他的p本地隊列的協程搶占過來運作
    • hand off:如果一個p執行的協程阻塞(執行io操作),排程單元p就會建立或喚醒一個新的線程(m)來執行之前p的本地協程隊列
  • 利用并行
    • 設定GOMAXPROCS值設定P的并行數
  • 搶占
    • 以前的c routine線程和cpu綁定,直到釋放才能讓新的線程擁有cpu資源,go-routine使用搶占機制,每個線程最多隻能固定時間占有
  • 全局G隊列
    • 其他的p排程器在空閑時優先從其他的p排程器的本地隊列擷取協程,然後才會在全局隊列中擷取協程
import (
	"fmt"
	"time"
)

func newTask() {
	i := 0
	for {
		i++
		fmt.Printf("go-routine:i=%d\n", i)
		time.Sleep(1 * time.Second)
	}
}

func main() {
	go newTask()

	i := 0
	for {
		i++
		fmt.Printf("main routine: i= %d\n", i)
		time.Sleep(1 * time.Second)
	}
}
//主程序退出,子程序也會結束 runtime.Goexit()結束整個子程序
           

channel

無buffer

//定義方法
ch := make(chan,tpye,capaticy)
//寫入值
ch<-value
//讀取值
<-ch
value:=<-ch
value,ok:=<-ch
//ok表示是否讀取成功
import (
	"fmt"
	"time"
)

func main() {
	//定義channel
	ch := make(chan int)
	go func() {
		defer fmt.Println("目前routine結束")
		fmt.Println("goroutine...")
		ch <- 10
		time.Sleep(1 * time.Second)
	}()
	value := <-ch
	fmt.Println("value = ",value)
	time.Sleep(1*time.Second)
	fmt.Println("主程序結束")
}
/*
goroutine...
value =  10
主程序結束
目前routine結束
*/
           
  • 可以看到主程序結束之後才會列印go-routine結束,說明是并行,如果主函數裡面不睡眠1s,可能不會列印“目前routine結束”
  • (無緩沖管道情況下)當讀取管道的一方讀取時,如果管道無資料則會阻塞等待資料發送方發送資料

有buffer

package main

import (
	"fmt"
	"time"
)

func main() {
	ch := make(chan int,5)
	//檢視目前元素數量和容量
	fmt.Println("len(ch)= ",len(ch),"cap(ch)=",cap(ch))
	go func(){
		defer fmt.Println("子循環結束")
		for i:=0;i<7;i++{
			ch<-i
			// time.Sleep(1*time.Second)
		}
	}()

	for i:=0;i<8;i++{
		value:=<-ch
		fmt.Println("value=",value)
		time.Sleep(1*time.Second)
	}
	fmt.Println("主程序結束")	
}
/*
value= 0
value= 1
子循環結束
value= 2
value= 3
value= 4
value= 5
value= 6
*/
           
  • 讀取的數量必須小于等于寫入的數量(非buffer數量),不然會造成死鎖
  • 讀取的時候可以寫入,但是寫入的最大數量是buffer量

管道關閉

import (
	"fmt"
	"time"
)

func main() {
	ch := make(chan int)
	go func() {
		defer fmt.Println("子程序關閉")
		for i := 0; i < 5; i++ {
			ch <- i
		}
		close(ch)
	}()
	time.Sleep(2 * time.Second)
	for {
		if data, ok := <-ch; ok {
			fmt.Println(data)
		} else {
			break
		}
	}
	fmt.Println("main finished")
	
}
/*
0
1
2
3
4
main finished
*/
           
  • 當子程序寫完之後調用close,這時管道并不會直接關閉,當讀取完成之後,才會關閉
  • 可以使用range ch來讀取資料

select 和 channel

import (
	"fmt"
	// "time"
)
func fibo(ch,out chan int){
	x,y:=0,1
	for{
		select{
		case ch<-x:
			x,y = y,x+y
		case <-out:
			fmt.Println("quit")
			return
		}

	}
}
func main() {
	ch := make(chan int)
	out := make(chan int)
	go func(){
		for i:=0;i<10;i++{
			fmt.Println(<-ch)
		}
		out<-0
	}()
	fibo(ch,out)
}
/*
0
1
1
2
3
5
8
13
21
34
quit
*/
           
  • select可以讓多個channel選擇執行
    1. 所有channel表達式都會被求值、所有被發送的表達式都會被求值。求值順序:自上而下、從左到右.結果是選擇一個發送或接收的channel,無論選擇哪一個case進行操作,表達式都會被執行。RecvStmt左側短變量聲明或指派未被評估。
    2. 如果有一個或多個IO操作可以完成,則Go運作時系統會随機的選擇一個執行,否則的話,如果有default分支,則執行default分支語句,如果連default都沒有,則select語句會一直阻塞,直到至少有一個IO操作可以進行.
    3. 除非所選擇的情況是預設情況,否則執行相應的通信操作。
    4. 如果所選case是具有短變量聲明或指派的RecvStmt,則評估左側表達式并配置設定接收值(或多個值)。
    5. 執行所選case中的語句