go goroutine

進程和線程react

  • 進程是程序在操做系統中的一次執行過程,系統進行資源分配和調度的 一個獨立單位。
  • 線程是進程的一個執行實體,是CPU調度和分派的基本單位,它是比進程更 小的能獨立運行的基本單位。
  • 一個進程能夠建立和撤銷多個線程;同一個進程中的多個線程之間能夠併發執行。

 

 

併發和並行git

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

 

協程和線程github

  • 協程:獨立的棧空間,共享堆空間,調度由用戶本身控制,本質上有點相似於 用戶級線程,這些用戶級線程的調度也是本身實現的。
  • 線程:一個線程上能夠跑多個協程,協程是輕量級的線程。

能夠說,協程與線程主要區別是它將再也不被內核調度,而是交給了程序本身而線程是將本身交給內核調度,因此也不難理解golang中調度器的存在。golang

示例:多線程

package main

import "fmt"
import "time"

func test() {
	var i int
	for {
		fmt.Println(i)
		time.Sleep(time.Second)
		i++
	}
}

func main() {
	go test()
	for {
		fmt.Println("i' running in main")
		time.Sleep(time.Second)
	}
}

 

goroutine調度模型併發

golang的goroutine是如何實現的?  知乎上一篇介紹文章。ui

  • M: 表明真正的內核OS線程,和POSIX裏的thread差很少,真正幹活的人。
  • G: 表明一個goroutine,它有本身的棧,instruction pointer和其餘信息(正在等待的channel等等),用於調度。
  • P: 表明調度的上下文,能夠把它看作一個局部的調度器,使go代碼在一個線程上跑,它是實現從N:1到N:M映射的關鍵。

圖中看,有2個物理線程M,每個M都擁有一個context(P),每個也都有一個正在運行的goroutine。
P的數量能夠經過GOMAXPROCS()來設置,它其實也就表明了真正的併發度,即有多少個goroutine能夠同時運行。
圖中灰色的那些goroutine並無運行,而是出於ready的就緒態,正在等待被調度。P維護着這個隊列(稱之爲runqueue)。

圖中看到,當一個OS線程M0陷入阻塞時,P轉而在OS線程M1上運行。調度器保證有足夠的線程來運行因此的context P。spa

 三者關係的宏觀的圖爲:操作系統

 

 

如何設置golang運行的cpu核數線程

package main

import (
	"fmt"
	"runtime"
)

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

備註:go1.8版本以後能夠不用設置,默認使用全部CPU核數。

 

鎖示例:

package main

import (
	"fmt"
	"sync"
	"time"
)

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 < 16; 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()
}

 

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

 

Go併發原理 https://i6448038.github.io/2017/12/04/golang-concurrency-principle/

Golang非CSP併發模型外的其餘並行方法總結 https://i6448038.github.io/2018/12/18/Golang-no-csp/

相關文章
相關標籤/搜索