json
package main
import(
"fmt"
"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"}}
jsonStr,err:=json.Marshal(user)
if err!=nil{
fmt.Println("error",err)
return
}
fmt.Printf("jsonStr= %s\n",jsonStr)
userTest :=User{}
err=json.Unmarshal(jsonStr,&userTest)
if err!=nil{
fmt.Println("error",err)
return
}
fmt.Printf("%v\n",userTest)
}
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)
}
}
channel
无buffer
ch := make(chan,tpye,capaticy)
ch<-value
<-ch
value:=<-ch
value,ok:=<-ch
import (
"fmt"
"time"
)
func main() {
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("主进程结束")
}
- 可以看到主进程结束之后才会打印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
}
}()
for i:=0;i<8;i++{
value:=<-ch
fmt.Println("value=",value)
time.Sleep(1*time.Second)
}
fmt.Println("主进程结束")
}
- 读取的数量必须小于等于写入的数量(非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")
}
- 当子进程写完之后调用close,这时管道并不会直接关闭,当读取完成之后,才会关闭
- 可以使用range ch来读取数据
select 和 channel
import (
"fmt"
)
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)
}
- select可以让多个channel选择执行
- 所有channel表达式都会被求值、所有被发送的表达式都会被求值。求值顺序:自上而下、从左到右.结果是选择一个发送或接收的channel,无论选择哪一个case进行操作,表达式都会被执行。RecvStmt左侧短变量声明或赋值未被评估。
- 如果有一个或多个IO操作可以完成,则Go运行时系统会随机的选择一个执行,否则的话,如果有default分支,则执行default分支语句,如果连default都没有,则select语句会一直阻塞,直到至少有一个IO操作可以进行.
- 除非所选择的情况是默认情况,否则执行相应的通信操作。
- 如果所选case是具有短变量声明或赋值的RecvStmt,则评估左侧表达式并分配接收值(或多个值)。
- 执行所选case中的语句