Go開發[八]goroutine和channel

進程和線程

進程是程序在操做系統中的一次執行過程,系統進行資源分配和調度的一個獨立單位。golang

線程是進程的一個執行實體,是CPU調度和分派的基本單位,它是比進程更小的能獨立運行的基本單位。安全

一個進程能夠建立和撤銷多個線程;同一個進程中的多個線程之間能夠併發執行.多線程

併發和並行

多線程程序在一個核的cpu上運行,就是併發併發

多線程程序在多個核的cpu上運行,就是並行函數

協程和線程

協程:獨立的棧空間,共享堆空間,調度由用戶本身控制,本質上有點相似於用戶級線程,這些用戶級線程的調度也是本身實現的ui

線程:一個線程上能夠跑多個協程,協程是輕量級的線程。操作系統

goroutine調度模型

簡單的例子線程

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

設置golang運行的cpu核數

go1.8版本以上默認設置了unix

package main

import (
    "fmt"
    "runtime"
)

func main() {
    num := runtime.NumCPU()
    runtime.GOMAXPROCS(num)
    fmt.Println(num)
}

不一樣goroutine之間通信

全局變量和鎖同步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

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

channel初始化

使用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

goroutine和channel相結合

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

chan之間的同步

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

chan的關閉

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) //謹防死循環
   }
}

for range遍歷chan

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

chan的只讀和只寫

a. 只讀chan的聲明

Var 變量的名字 <-chan int
Var readChan <- chan int

b. 只寫chan的聲明

Var 變量的名字 chan<- int
Var writeChan chan<- int

對chan進行select操做

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中使用recover

應用場景,若是某個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)
}
相關文章
相關標籤/搜索