天天看点

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中的语句