并发Goroute、定时器、信号处理、单元测试
Goroutine(輕量級的線程,開線程沒有數量限制)
1.進程和線程
A.進程是程序在操作系統中的一次執行過程,系統進行資源分配和調度的一個獨立單位。
B.線程是進程的一個執行實體,是CPU調度和分派的基本單位,它是比進程更小的能獨立運行的基本單位。
C.一個進程可以創建和撤銷多個線程;同一個進程中的多個線程之間可以并發執行。
2、進程和線程
ngix是多進程的單線程程序
?3.并發和并行
? A.多線程程序在一個核的cpu上運行,就是并發。go多線程的切換都是在用戶態操作的,不像其他語言先切換到內核態,完成線程切換,然后返回用戶態繼續執行程序。
? B.多線程程序在多個核的cpu上運行,就是并行
4.協程和線程
協程:獨立的棧空間,共享堆空間,調度由用戶自己控制,本質上有點類似于用戶級線程,這些用戶級線程的調度也是自己實現的
線程:一個線程上可以跑多個協程,協程是輕量量級的線程。一個線程可以跑多個Goroutine。
5.goroutine調度模型
M是物理線程,P是資源,G是Goroutine
?
如果有IO操作時,會新起一個線程等待IO操作的Goroutine
?
6.如何設置golang運行的cpu核數
1.5之前go需要手動設置程序執行的內核數,1.5之后go自動設置
package mainimport ("fmt""runtime" ) func main() { num := runtime.NumCPU() //查看有幾個內核 fmt.Printf("cpu num:%d\n", num) runtime.GOMAXPROCS(1) //設置有程序用幾個內核執行 }7、不同goroutine之間進行通訊
a.全局變量
package mainimport ("fmt""time" )var exits [3]boolfunc calc(index int) {for i := 0; i < 1000; i++ {time.Sleep(time.Millisecond) //休息1毫秒 }exits[index] = true }func main() {start := time.Now().UnixNano()go calc(0) //起一個goroutine,不用go就是串行go calc(1)go calc(2)for { //保證主線程不掛斷,子線程才能執行if exits[0] && exits[1] && exits[2] {break}time.Sleep(time.Millisecond)}end := time.Now().UnixNano()fmt.Printf("finished, cost:%d ms\n", (end-start)/1000/1000) } View Codeb.鎖同步
package mainimport ("fmt""sync""time" )func calc(index int, waitGroup *sync.WaitGroup) {for i := 0; i < 1000; i++ {time.Sleep(time.Millisecond)}waitGroup.Done() //釋放一把鎖 }func main() {var waitGroup sync.WaitGroup //等待一組Goroutine執行完畢start := time.Now().UnixNano()for i := 0; i < 3; i++ {waitGroup.Add(1) //添加一把鎖go calc(i, &waitGroup)}waitGroup.Wait()end := time.Now().UnixNano()fmt.Printf("finished, cost:%d ms\n", (end-start)/1000/1000) } View Codec.Channel(oroutine和channel相結合)
package mainimport ("fmt""time" )func () {var intChan chan int = make(chan int, 1) //如果是0就是沒有緩沖區的管道,必須有程序來取才能放進去 go func() {intChan <- 10}()result := <-intChanfmt.Printf("result:%d\n", result) } View Code?8.goroutine中使用recover
應用場景,如果某個goroutine panic了,而且這個goroutine 里面沒有捕獲(recover),那么整個進程就會掛掉。所以,好的習慣是每當go產生一個goroutine,就需要寫下recover。
package mainimport ("fmt""time" )func calc() {defer func() { //defer必須放置在最前面,才能捕獲后面所有的panic,程序退出時執行defererr := recover() //捕獲goroutine錯誤if err != nil {fmt.Println(err)}}()var p *int*p = 100 }func main() {go calc()time.Sleep(time.Second * 3)fmt.Println("progress exited") } View Code?
Channel
1、channel概念?
- 類似unix中管道(pipe)?
- 先進先出
- 線程安全,多個goroutine同時訪問,不需要加鎖
- channel是有類型的, 一個整數的channel只能存放整數
2、?channel聲明?
var 變量量名?chan 類型?
var test chan int?
var test chan string?
var test chan map[string]string?
var test chan stu
3、channel初始化
使用make進行初始化,比如:?
var test chan int
test = make(chan int, 10)?
var test chan string
test = make(chan string, 10)
4、channel基本操作
1. 從channel讀取數據:
var testChan chan int
testChan = make(chan int, 10)
var a int
a = <-?testChan
2. 從channel寫 入數據:
var testChan chan int
testChan = make(chan int, 10)
var a int = 10
testChan <-?a
5.帶緩沖區的channel
? 1.如下所示,testChan只能放 一個元素:
?? var testChan chan int
?? testChan = make(chan int)
?? var a int
? ?a = <- testChan
? 2.如下所示,testChan是帶緩沖區的chan, 一次可以放10個元素:
? ? var testChan chan int
?? testChan = make(chan int, 10)
? ?var a int = 10
? ?testChan <- a
?6. channel阻塞
package mainimport ("fmt""time" )func sendData(ch chan string) {var i intfor {var str stringstr = fmt.Sprintf("stu %d", i)fmt.Println("write:", str)ch <- stri++} }func main() {ch := make(chan string)go sendData(ch)time.Sleep(100 * time.Second) } channel阻塞7.chan之間的同步
package mainimport ("fmt""time" )func sendData(ch chan string) {ch <- "Washington"ch <- "Tripoli"ch <- "London"ch <- "Beijing"ch <- "Tokio" }func getData(ch chan string) {var input stringfor {input = <-chfmt.Println(input)} }func main() {ch := make(chan string)go sendData(ch)go getData(ch)time.Sleep(100 * time.Second) } View Code8.for range遍歷chan
package mainimport ("fmt""time" )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)} }func main() {ch := make(chan string)go sendData(ch)go getData(ch)time.Sleep(100 * time.Second) } View Code9.chan的關閉??
1.使用內置函數close進行關閉,chan關閉之后,for range遍歷chan中已經存在的元素后結束 ?
2.使用內置函數close進行關閉,chan關閉之后,沒有使用for range的寫法需要使用,v, ok := <- ch進行判斷chan是否關閉?
10.chan的只讀和只寫 ?
a.只讀chan的聲明 ??
Var 變量量的名字 <-chan int???
Var readChan <- chan int
? ? b.只寫chan的聲明 ??
Var 變量量的名字 chan<- int ??
Var writeChan chan<- int
11. 對chan進行select操作
? Select {
?? ? case u := <- ch1:
?? ???? case e := <- ch2:
?? ???? default:?
?}
定時器?
1、定時器的使用
package main import ("fmt""time" )func main() {t := time.NewTicker(time.Second)for v := range t.C {fmt.Println("hello, ", v)} } View Code2、一次定時器
package mainimport ("fmt""time" )func main() {select {Case <- time.After(time.Second):fmt.Println(“after”)} } View Code3、超時控制
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")} } View Code?
信號處理
package mainimport ("fmt""os""os/signal""sync""syscall" )var waitGroup sync.WaitGroupfunc produce(ch chan<- string, exitChan chan bool) {var i intvar exit boolfor {str := fmt.Sprintf("hello %d", i)select { //select檢測哪個管道可寫或者可讀case ch <- str:case exit = <-exitChan:}if exit {fmt.Printf("user notify produce exited\n")break}}close(ch)waitGroup.Done() }func consume(ch <-chan string) {for {str, ok := <-chif !ok {fmt.Printf("ch is closed")break}fmt.Printf("value:%s\n", str)}waitGroup.Done() }func main() {// 在shell終端輸入 kill -SIGUSR2 ID 給程序輸入終止信號var ch chan string = make(chan string)var exitChan chan bool = make(chan bool, 1)var sinalChan chan os.Signal = make(chan os.Signal, 1)waitGroup.Add(2)signal.Notify(sinalChan, syscall.SIGUSR2)go produce(ch, exitChan)go consume(ch)<-sinalChan //讀取然丟棄exitChan <- truewaitGroup.Wait()} 信號處理單元測試
1.文件名必須以_test.go結尾
2.使用go test -v執行單元測試
calc.go
package test//對函數進行單元測試 func add(a int, b int) int {return a - b }func sub(a int, b int) int {return a - b } View Codecalc_test.go
package testimport ("testing" )func TestAdd(t *testing.T) { //TestAdd必須大寫的Test開頭result := add(2, 8) //測試add函數if result != 10 {t.Fatalf("add is not right") //致命錯誤記錄return}t.Logf("add is right") //記錄一些常規信息 }func TestSub(t *testing.T) {result := sub(2, 8)if result != -6 {t.Fatalf("add is not right")return}t.Logf("add is right") } View Code?
?
?
?
?
?
?
轉載于:https://www.cnblogs.com/domestique/p/8410313.html
《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀總結
以上是生活随笔為你收集整理的并发Goroute、定时器、信号处理、单元测试的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 茶香账簿小程序开发进度(1)
- 下一篇: C#强化系列:HttpModule,Ht