進程是程序在操做系統中的一次執行過程,系統進行資源分配和調度的一個獨立單位。golang
線程是進程的一個執行實體,是CPU調度和分派的基本單位,它是比進程更小的能獨立運行的基本單位。安全
一個進程能夠建立和撤銷多個線程;同一個進程中的多個線程之間能夠併發執行.多線程
多線程程序在一個核的cpu上運行,就是併發併發
多線程程序在多個核的cpu上運行,就是並行函數
協程:獨立的棧空間,共享堆空間,調度由用戶本身控制,本質上有點相似於用戶級線程,這些用戶級線程的調度也是本身實現的ui
線程:一個線程上能夠跑多個協程,協程是輕量級的線程。操作系統
簡單的例子線程
package main import "fmt" import "time" func test() { var i int for { fmt.Println(i) time.Sleep(time.Second*5) i++ } } func main() { go test() for { fmt.Println("i' running in main") time.Sleep(time.Second) } }
go1.8版本以上默認設置了unix
package main import ( "fmt" "runtime" ) func main() { num := runtime.NumCPU() runtime.GOMAXPROCS(num) fmt.Println(num) }
全局變量和鎖同步code
package main import ( "fmt" "time" "sync" ) var ( m = make(map[int]uint64) lock sync.Mutex ) type task struct { n int } func calc(t *task) { var sum uint64 sum = 1 for i:=1;i<t.n;i++{ sum *= uint64(i) } fmt.Println(t.n,sum) lock.Lock() m[t.n] = sum lock.Unlock() } func main() { for i :=0;i<20;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
a. 相似unix中管道(pipe)
b. 先進先出
c. 線程安全,多個goroutine同時訪問,不須要加鎖
d. channel是有類型的,一個整數的channel只能存放整數
var 變量名 chan 類型
var test chan int
var test chan string
var test chan map[string]string
var test chan stu
var test chan *stu
使用make進行初始化,好比:
var test chan int
test = make(chan int, 10)
var test chan string
test = make(chan string, 10)
從channel讀取數據
var testChan chan int testChan = make(chan int, 10) var a int a = <- testChan
向channel寫入數據
var testChan chan int testChan = make(chan int, 10) var a int = 10 testChan <- a
package main import ( "fmt" "time" ) func write(ch chan int) { for i := 0; i < 100; i++ { ch <- i fmt.Println("put data:", i) } } func read(ch chan int) { for { var b int b = <-ch fmt.Println(b) time.Sleep(time.Second) } } func main() { intChan := make(chan int, 10) //testChan是帶緩衝區的chan,一次能夠放10個元素 go write(intChan) go read(intChan)//不讀的話會引起阻塞 time.Sleep(10 * time.Second) }
package main import "fmt" func send(ch chan int, exitChan chan struct{}) { for i := 0; i < 10; i++ { ch <- i } close(ch) var a struct{} exitChan <- a } func recv(ch chan int, exitChan chan struct{}) { for { v, ok := <-ch if !ok { break } fmt.Println(v) } var a struct{} exitChan <- a } func main() { var ch chan int ch = make(chan int, 10) exitChan := make(chan struct{}, 2) go send(ch, exitChan) go recv(ch, exitChan) var total = 0 for _ = range exitChan { total++ if total == 2 { break } } }
例子:
package main import ( "fmt" ) //計算1000之內的素數 func calc(taskChan chan int, resChan chan int, exitChan chan bool) { for v := range taskChan { flag := true for i := 2; i < v; i++ { if v%i == 0 { flag = false break } } if flag { resChan <- v } } fmt.Println("exit") exitChan <- true } func main() { intChan := make(chan int, 100) resultChan := make(chan int, 100) exitChan := make(chan bool, 8) go func() { for i := 0; i < 1000; i++ { intChan <- i } close(intChan) }() for i := 0; i < 8; i++ { go calc(intChan, resultChan, exitChan) } //等待全部計算的goroutine所有退出 go func() { for i := 0; i < 8; i++ { <-exitChan fmt.Println("wait goroute ", i, " exited") } close(resultChan) }() for v := range resultChan { //fmt.Println(v) _ = v } }
1.使用內置函數close進行關閉,chan關閉以後,for range遍歷chan中已經存在的元素後結束
2.使用內置函數close進行關閉,chan關閉以後,沒有使用for range的寫法須要使用,v, ok := <- ch進行判斷chan是否關閉
package main import "fmt" func main() { var ch chan int ch = make(chan int, 10) for i := 0; i < 10; i++ { ch <- i } close(ch) for { var b int b,ok := <-ch if ok == false { fmt.Println("chan is close") break } fmt.Println(b) //謹防死循環 } }
package main import "fmt" func main() { var ch chan int ch = make(chan int, 1000) for i := 0; i < 1000; i++ { ch <- i } close(ch) for v := range ch { fmt.Println(v) } }
a. 只讀chan的聲明
Var 變量的名字 <-chan int Var readChan <- chan int
b. 只寫chan的聲明
Var 變量的名字 chan<- int Var writeChan chan<- int
select { case u := <- ch1: case e := <- ch2: default: }
下面是死鎖了,阻塞了
package main import "fmt" func main() { var ch chan int ch = make(chan int,10) for i :=0;i<10;i++{ ch <- i } for { var b int b = <-ch fmt.Println(b) } }
select解決阻塞
//@Time : 2018/2/1 22:14 //@Author: ningxin package main import ( "fmt" "time" ) func main() { var ch chan int ch = make(chan int,10) for i :=0;i<10;i++{ ch <- i } for { select{ case v:= <-ch: fmt.Println(v) default: fmt.Println("get data timeout") time.Sleep(time.Second) } } }
package main import ( "fmt" "time" ) func queryDb(ch chan int) { time.Sleep(time.Second) ch <- 100 } func main() { ch := make(chan int) go queryDb(ch) t := time.NewTicker(time.Second) select { case v := <-ch: fmt.Println("result", v) case <-t.C: fmt.Println("timeout") } }
超時控制
package main import ( "fmt" "runtime" "time" ) func main() { num := runtime.NumCPU() runtime.GOMAXPROCS(num - 1) for i := 0; i < 16; i++ { go func() { for { t := time.NewTicker(time.Second) select { case <-t.C: fmt.Println("timeout") } t.Stop() } }() } time.Sleep(time.Second * 100) }
應用場景,若是某個goroutine panic了,並且這個goroutine裏面沒有捕獲(recover),那麼整個進程就會掛掉。因此,好的習慣是每當go產生一個goroutine,就須要寫下recover
package main import ( "fmt" "runtime" "time" ) func test() { defer func() { if err := recover(); err != nil { fmt.Println("panic:", err) } }() var m map[string]int m["stu"] = 100 } func calc() { for { fmt.Println("i'm calc") time.Sleep(time.Second) } } func main() { num := runtime.NumCPU() runtime.GOMAXPROCS(num - 1) go test() for i := 0; i < 2; i++ { go calc() } time.Sleep(time.Second * 10000) }