一、goroute
理论
进程:进程是程序在操作系统中的一次执行过程,系统进行资源分配和调度的一个独立单位
线程:线程是进程的一个执行实体,是CPU调度和分配的基本单位,是比进程跟小的能独立运行的基本单位
进程与线程的关系:一个进程一颗创建和撤销多个线程,同一个进程中的多个线程之间可以并发执行
协程:独立的栈空间,共享堆空间,调度由用户自己控制,本质上优点类似用户级线程,这些用户线程的调用也是自己实现的
协程和线程的关系:一个线程可以跑多个协程,协程是轻量级线程
并发:同一时间段内执行多个操作
并行:同一时刻执行多个操作
Gorouteine多线程:线程是有操作系统进行管理,处于内核态,线程之间进行切换需要发生用户态到内核态的切换
当系统中运行大量线程,系统会变得非常慢,用户态的线程支持大量线程的创建,也叫协程或者goroutine
Goroutine使用介绍
//创建goroutine func hello() { fmt.Println("hello goroutine") } func main() { go hello() fmt.Prinln("main thread terminate") time.Sleep(time.Second) //goroutine会随着主线程的结束而结束,需要等待1s } //启动多个goroutine func hello(i int) { fmnt.Println("hello goroutine",i) } func test() { for i := 0<10;i++{ go hello(i) } } func main() { test() time.Sleep(time.Second) } //多核控制 var i int func calc() {for {i++)} func main() { cpu := runtime.NumCPU() //显示当前CPU有多少核 fmt.Prinln("cpu",cpu) runtime.GOMAXPROCS(1) //只允许调用一个cpu内核 for i := 0;i<10;i++ { go calc() } time.Sleep(time.Hour) }
Goroutine原理解析
概念:一个操作系统线程对应用户态多个goroutine,可同时使用多个操作系统线程,操作系统线程对goroutine是多对多关系
调度:当操作系统线程需要执行时,开始逐个执行goroutine
蓝色goroutine执行结束后再执行灰色,当前有两个线程在执行
系统调用的处理
二、goroutine之间的通信
全局变量和锁同步
import ( "fmt" "time" "sync" ) var m make(map[int]uint64) type tesk struct { n int } func calc(t *task) { var sum uint64 sum = 1 for i := 1;i<t.n;i++ { sum *= uint64(i) } lock.Lock() m[t.n] = sum lockUnlock() } func main() { for i := 0;i<100;i++ { t := &task{n:i} go calc(t) } time.Sleep(10 * time.Second) lock.Lock() for k,v := range m { fmt.Printf("%d!=%v\n",k,v) } lock.Unlock() }
channel(队列管道)
介绍:本质上是一个队列/容器,先进先出,类似unix中的管道pipe
定义:需要指定勇气中元素的类型 var 变量名 chan 数据类型
var test chan int var test chan string var test chan map[string]string var test chan stcurt var test chan *stcurt //定义一个地址,只能传入地址
入队:a <- 100
出队:data := <-a
//test1 func main() { var c chan int fmt.Printf("c =%v\n",c) c = make(chan int,10) //如果不分配空间,则入队一直阻塞,运行报错 fmt.Printf("c = %v\n",c) c <- 100 //<-c不给变量存储则为丢弃这个元素 data := <-c fmt.Println("data",data) } //test2 type student struct { name string } func main() { var stuChan chan student stuChan = make(chan *Student,10) stu := student{name:"stu01"} stuChan <- stu } //test3 func produce(c chan int) { c <- 1000 fmt.Println("produce finished") } func consume (c chan int) { data := <-c fmt.Println("data",data) } func main() { var c chan int fmt.Printf("c = %v\n",c) c = make(chan int) go produce(c) go consume(c) time.Sleep(time.Second * 3) } //不带缓冲区的队列,有出队的时候才能入队,否则入队失败
channel练习
//chan和goroutine同步 func hello(c chan bool){ fmt.Println("hello goroutine") c <- true } func main(){ var exitChan chan bool exitChan = make(chan bool) go hello(exitChan) fmt.Println("main thread terminate") <-exitChan } //单向chan func sendData(sendch chan<- int) { chan<- 表示之能入队 sendch <- 10 } func readData(sendch <-chan int) { <- chan 表示只能出队 data := <-sendch fmt.Println("data:", data) } func main() { chnl := make(chan int) go sendData(chnl) readData(chnl) } //chan关闭 func producer(chnl chan int) { for i := 0; i < 10; i++ { chnl <- i } close(chnl) //关闭chan } func main() { ch := make(chan int) go producer(ch) for { v, ok := <-ch if ok == false { //判断chan是否关闭,如果无法判断管道是否关闭会一直取出默认值0保持死循环 fmt.Println("chan is closed") break } fmt.Println("Received", v, ok) } } //for range func produer(chnl chan int) { for i := 0; i < 10; i++ { chnl <- i } close(chnl) } func main() { ch := make(chan int) go produer(ch) for v := range ch { //chan关闭无数据后自动运行结束 fmt.Println("receive:", v) } } //带缓冲区的chanel func write( ch chan int) { for i:= 0;i<5;i++ { ch <- i fmt.Println("successfully wrote",i,"to ch") } close(ch) } func main() { ch := make(chan int,2) go write(ch) time.Sleep(2 * time.Second) for v := range ch { fmt.Println("read value",v,"from ch") time.Sleep(2 *time.Second) } } //channel的长度和容量 func main() { ch := make(chan string,3) ch <- "naveen" ch <- "paul" fmt.Println("capacity is",cap(ch)) fmt.Println("length is",len(ch)) fmt.Println("read value",<-ch) fmt.Println("new length is",len(ch)) } //waitgroup等待一组goroutine结束,使用不带缓冲区的channel实现 func process(i int,ch chan bool) { fmt.Println("started Goroutine",i) time.Sleep(2 * time.Second) fmt.Printf("Goroutine %d ended\n",i) ch <- true } func main() { no := 3 exitChan := make(chan bool,no) for i := 0;i<no;i++ { go process(i,exitChan) } for i:= 0;i<no;i++ { <exitChan } fmt.Println("All goroutines finshed executing") } //使用sync-WaitGroup实现 func process(i int,wg *sync.WaitGroup) { fmt.Println("started Goroutine",i) time.Sleep(2 * time.Second) fmt.Printf("Goroutine %d ended\n",i) wg.Done() //wg-1 } func main() { no := 3 var wq sync.WaitGroup //定义一个wgh,默认为0 for i := 0; i<no; i++ { wg.Add(1) wg+1 go process(i,&wg) } wg.Wait() //当wg为0时才会向下执行 fmt.Println("All goroutines finished executing“) }”
三、select和线程之间的安全
select语义介绍
定义:多channel操作,同时监听一个或多个channel,直到其中一个channel ready,如果其中合格多个chgannel同时ready,随机选择一个进行操作,语法和weitch case类似,只获取一个数值就结束
func server1(ch chan string) { time.Sleep(time.Second * 3) ch <- "response from server1" } func server2(ch chan string) { time.Sleep(time.Second) ch <-"response from server2") } func main() { output1 := make(chan string) output2 := make(chan stirng) go server1(output1) go server2(output2) //s1 := <-output1 //等待3s //fmt.Println("s1:",s1) //s2 := <-output2 //等待1s后等待s1结束再执行 //fmt.Println("s2",s2) select { //最终只取一个channel的结果 case s1 := <-output1: fmt.Println("s1:",s1) case s2 := <-output2: fmt.Prinln("s2:",s2) } }
default分支:放case分支的channel都没有ready的话,执行default,用来判断channel是否满了或者为空
//test1 func server1(ch chan string) { time.Sleep(time.Second * 3) ch <- "resdponse from server1" } func server2(ch chan string) { time.Sleep(time.Second) ch <- "response from server2" } func main() { output1 := make(chan string) output2 := make(chan string) go server1(output1) go server2(output2) select { //最终只取一个channel的结果 case s1 := <- output1: fmt.Println("s1;",s1) case s2 := <-output2: fmt.Println("s2:",s2) default: fmt.Println("run default") //因为s1和s2需要等待,default瞬间执行 } } //test2 func write(ch chan string) { for { select { case ch <- "hello": fmt.,Println("write succ") default: fmt.Println("channel is full") } time.Sleep(time.Millisecond * 500) } } func main() { output1 := make(chan string,10) go write(output1) for s := range.output1 { fmt.Println("recv:"s) time.Sleep(time.Second) } }