1.進程和線程
A.進程是程序在操做系統中的一次執行過程,系統進行資源分配和調度的一個獨立單位。
B.線程是進程的一個執行實體,是CPU調度和分派的基本單位,它是比進程更小的能獨立運行的基本單位。
C.一個進程能夠建立和撤銷多個線程;同一個進程中的多個線程之間能夠併發執行。golang
二、進程和線程shell
ngix是多進程的單線程程序安全
3.併發和並行
A.多線程程序在一個核的cpu上運行,就是併發。go多線程的切換都是在用戶態操做的,不像其餘語言先切換到內核態,完成線程切換,而後返回用戶態繼續執行程序。
B.多線程程序在多個核的cpu上運行,就是並行多線程
4.協程和線程
協程:獨立的棧空間,共享堆空間,調度由用戶本身控制,本質上有點相似於用戶級線程,這些用戶級線程的調度也是本身實現的
線程:一個線程上能夠跑多個協程,協程是輕量量級的線程。一個線程能夠跑多個Goroutine。併發
5.goroutine調度模型ide
M是物理線程,P是資源,G是Goroutine函數
若是有IO操做時,會新起一個線程等待IO操做的Goroutine單元測試
6.如何設置golang運行的cpu核數測試
1.5以前go須要手動設置程序執行的內核數,1.5以後go自動設置spa
package main
import (
"fmt" "runtime" ) func main() { num := runtime.NumCPU() //查看有幾個內核 fmt.Printf("cpu num:%d\n", num) runtime.GOMAXPROCS(1) //設置有程序用幾個內核執行 }
七、不一樣goroutine之間進行通信
a.全局變量
package main import ( "fmt" "time" ) var exits [3]bool func 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) }
b.鎖同步
package main import ( "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) }
c.Channel(oroutine和channel相結合)
package main import ( "fmt" "time" ) func () { var intChan chan int = make(chan int, 1) //若是是0就是沒有緩衝區的管道,必須有程序來取才能放進去 go func() { intChan <- 10 }() result := <-intChan fmt.Printf("result:%d\n", result) }
8.goroutine中使用recover
應用場景,若是某個goroutine panic了,並且這個goroutine 裏面沒有捕獲(recover),那麼整個進程就會掛掉。因此,好的習慣是每當go產生一個goroutine,就須要寫下recover。
package main import ( "fmt" "time" ) func calc() { defer func() { //defer必須放置在最前面,才能捕獲後面全部的panic,程序退出時執行defer err := 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") }
一、channel概念
二、 channel聲明
var 變量量名 chan 類型
var test chan int
var test chan string
var test chan map[string]string
var test chan stu
三、channel初始化
使用make進行初始化,好比:
var test chan int
test = make(chan int, 10)
var test chan string
test = make(chan string, 10)
四、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
package main import ( "fmt" "time" ) func test() { var intChan chan int = make(chan int, 1) //若是是0就是沒有緩衝區的管道,必須有程序來取才能放進去 go func() { intChan <- 10 }() result := <-intChan fmt.Printf("result:%d\n", result) } func testNoBufChan() { var intChan chan int = make(chan int, 2) go func() { fmt.Printf("begin input to chan\n") intChan <- 10 intChan <- 10 fmt.Printf("end input to chan\n") }() //result := <- intChan //fmt.Printf("result:%d\n", result) time.Sleep(10 * time.Second) } func main() { test() testNoBufChan() }
package main import ( "fmt" "sync" ) var waitGroup sync.WaitGroup func produce(ch chan<- string) { //生產者 ch <- "hello1" ch <- "hello2" ch <- "hello3" close(ch) //關閉管道 waitGroup.Done() } func consume(ch <-chan string) { //消費者 for { str, ok := <-ch //判斷管道是否關閉 if !ok { fmt.Printf("ch is closed") break } fmt.Printf("value:%s\n", str) } waitGroup.Done() } func main() { //生產者消費者模型 var ch chan string = make(chan string) waitGroup.Add(2) go produce(ch) go consume(ch) waitGroup.Wait() }
package main import ( "fmt" "time" ) func sendData(ch chan string) { var i int for { var str string str = fmt.Sprintf("stu %d", i) fmt.Println("write:", str) ch <- str i++ } } func main() { ch := make(chan string) go sendData(ch) time.Sleep(100 * time.Second) }
7.chan之間的同步
package main import ( "fmt" "time" ) 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) } } func main() { ch := make(chan string) go sendData(ch) go getData(ch) time.Sleep(100 * time.Second) }
8.for range遍歷chan
package main import ( "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) }
9.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:
}
一、定時器的使用
package main import ( "fmt" "time" ) func main() { t := time.NewTicker(time.Second) for v := range t.C { fmt.Println("hello, ", v) } }
二、一次定時器
package main import ( "fmt" "time" ) func main() { select { Case <- time.After(time.Second): fmt.Println(「after」) } }
三、超時控制
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" "os" "os/signal" "sync" "syscall" ) var waitGroup sync.WaitGroup func produce(ch chan<- string, exitChan chan bool) { var i int var exit bool for { 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 := <-ch if !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 <- true waitGroup.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 }
calc_test.go
package test import ( "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") }