文章目錄
-
- 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的本地協程隊列
- 利用并行
- 搶占
- 以前的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選擇執行
- 所有channel表達式都會被求值、所有被發送的表達式都會被求值。求值順序:自上而下、從左到右.結果是選擇一個發送或接收的channel,無論選擇哪一個case進行操作,表達式都會被執行。RecvStmt左側短變量聲明或指派未被評估。
- 如果有一個或多個IO操作可以完成,則Go運作時系統會随機的選擇一個執行,否則的話,如果有default分支,則執行default分支語句,如果連default都沒有,則select語句會一直阻塞,直到至少有一個IO操作可以進行.
- 除非所選擇的情況是預設情況,否則執行相應的通信操作。
- 如果所選case是具有短變量聲明或指派的RecvStmt,則評估左側表達式并配置設定接收值(或多個值)。
- 執行所選case中的語句