併發Goroute、定時器、信號處理、單元測試

Goroutine(輕量級的線程,開線程沒有數量限制)

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)
}
View Code

  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)
}
View Code

  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)
}
View Code

 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")
}
View Code

 

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

三、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()
}
View Code
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()
}
生產者消費者模型

 6. channel阻塞

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)
}
channel阻塞

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)
}
View Code

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)
}
View Code

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)
    }
}
View Code

二、一次定時器

package main

import (
    "fmt"
    "time"
)


func main() {
    select {
        Case <- time.After(time.Second):
        fmt.Println(「after」)
    }
}
View Code

三、超時控制

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 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 }
View Code

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")
}
View Code
相關文章
相關標籤/搜索