您好,登錄后才能下訂單哦!
進程和線程
1. 進程是程序在操作系統中的?次執?過程,系統進口資源分配和調度的一個獨力單位。
2. 線程是進程的一個執行實體,是CPU調度和分派的基本單位,它是?進程更?的能獨力運行的基本單位。
3. 一個進程可以創建和撤銷多個線程;同一個進程中的多個線程之間可以并發執行
Goroutine
并發和并行
多線程程序在一個核的CPU上運行,就是并發
多線程程序在多個核的CPU上運行,就是并發
設置GO運行的CPU核數
func Test1() { num := runtime.NumCPU() //設置程序運行占幾個核 runtime.GOMAXPROCS(num) fmt.Println(num) }
線程同步
sync.WaitGroup
package main import ( "fmt" "time" "sync" ) var wailtGroup sync.WaitGroup //sync 線程同步 func Test2(index int) { for i:=0;i<100;i++{ time.Sleep(time.Millisecond) } wailtGroup.Done() } func calc() { start :=time.Now().UnixNano() //Test2(0) for i:=0; i<3;i++{ go Test2(i) wailtGroup.Add(1) } #當wailtGroup 為0時,就會返回 wailtGroup.Wait() end := time.Now().UnixNano() fmt.Printf("finished,cost:%d ms \n",(end - start) / 1000 / 1000) } func main() { //Test1() calc() }
不同goroutine之間通信
全局變量和鎖同步
Channel
Channel
類似unix中的管道(pipe)
先進先出
線程安全,多個goroutine同時訪問,不需要加鎖
Channel是有類型的,一個整數的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
Channel初始化:
不帶緩沖區:默認就是0,必須有取值,才可以放入,不然就會阻塞!
帶緩沖區: 值>0
使用make進行初始化
var test chan int
test =make(chan int,10)
var test2 chan string
test = make(chan string,10)
Channel基本操作:
1.從channel讀取數據
testChan :=make(chan int,10) var a int a = <- testChan
2.從channel寫數據
testChan:=make(chan int,10) var a int= 10 testChan <- a
栗子:
1.初級操作
func test() { var intChan chan int = make(chan int,3) go func(){ fmt.Println("begin input to chan\n") intChan <- 20 intChan <- 20 intChan <- 20 fmt.Println("end input to chan\n") }() result := <- intChan fmt.Printf("--result:%d",result) time.Sleep(2*time.Second) } func main() { test() }
2.goroutine和channel結合
package main import ( "fmt" "time" ) func main() { ch := make(chan string) go sendData(ch) go getData(ch) //加time原因是讓2個go去執行, 因為主線程中的代碼是比goroutine先執行完畢的 time.Sleep(100 * time.Second) } func sendData(ch chan string) { ch <- "Washington" ch <- "Tripoli" ch <- "London" ch <- "Beijing" ch <- "Tokio" } func getData(ch chan string) { var input string for { input = <-ch fmt.Println(input) } }
for循環去遍歷chan
package main import ( "fmt" "time" ) func main() { ch := make(chan string) go sendData(ch) go getData(ch) time.Sleep(100 * time.Second) } func sendData(ch chan string) { ch <- "Washington" ch <- "Tripoli" ch <- "London" ch <- "Beijing" ch <- "Tokio" } func getData(ch chan string) { for input := range ch { fmt.Println(input) } }
Channel關閉
使用內置函數close進行關閉,chan關閉后。for range遍歷chan中已經存在的元素后結束
func rangegetData(ch chan string) { //另外用法,用來取管道中的數據 // for input:=range ch{ fmt.Printf("#%v\n",input) } }
使用內置函數close進行關閉,chan關閉后。沒有使用for range的寫法需要使用,v,ok := <-ch 進行判斷chan是否關閉
func getData(ch chan string) { //死循環 for { input,ok :=<- ch //ok是判斷管道是否關閉 if !ok{ fmt.Printf("管道關閉") break } fmt.Printf("channe_read:%s\n",input) } }
進階栗子:
func consumer(ch <-chan string){ for{ str,ok := <- ch if !ok{ fmt.Printf("ch is closed!!") break } fmt.Printf("value is %s \n",str) } } func main(){ var ch chan string = make(chan string) consumer(ch)}
Channel只讀/只寫
只讀chan聲明
var 變量名字 <-chan int var readChan <-chan int
只寫chan聲明
var 變量名字 chan<-int var writeChan chan<-int
Channel Select管理
注意:調度是隨機的
一個簡單的栗子:
for { str := fmt.Sprintf("hello %d",i) //select來管理管道 //調度是隨機的, select { case ch <- str: case exit = <-exitChan: } if exit{ fmt.Printf("user notify exitedd!!\n") break } }
定時器
規定時間后運行代碼
package main import ( "time" "fmt" ) func run() { t:=time.NewTicker(time.Second * 5) //t.C 是一個管道 for v:=range t.C{ fmt.Println("hello",v) } } func main() { run() }
只運行一次:
package main import ( "fmt" "time" ) func main() { select { case <- time.After(time.Second): fmt.Println("after") } }
超時控制 (可以用于檢測類似Mysql查詢超時等):
package main import ( "time" "fmt" ) func queryDB(ch chan int) { time.Sleep(time.Second * 1000) ch <- 100 } func main() { ch :=make(chan int,5) go queryDB(ch) //設置主線程運行時間, t := time.NewTicker(time.Second * 5 ) //隨機調度 select { //去ch中取,是否有數據, case <-ch: fmt.Println("reslt") case <-t.C: fmt.Printf("timeout!!!") } }
讀寫鎖:
寫鎖:sync.Mutex 是互斥鎖, 多個線程修改數據的適合,只有一個線程可以修改
讀鎖:sync.RWMutex 讀鎖,多個線程同時讀一份數據,可以并發的去執行
Go中使用recover
應?場景,如果某個goroutine panic了,?且這個goroutine??沒有捕獲(recover),那么整個進程就會掛掉。所以,好的習慣是每當go產??個goroutine,就需要寫下recover
package main import ( "time" "fmt" ) func calc() { //捕獲異常 //必須寫在最前面 defer func() { error :=recover() if error !=nil{ fmt.Println(error) } }() var p *int //p = new(int) *p = 100 } func main() { go calc() time.Sleep(time.Second * 2) fmt.Println("---") }
單元測試
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。