goroutine 分析 協程的調度和執行順序 併發寫

 

 

package main

import (
	"fmt"
	"runtime"
	"sync"
)

const N = 26

func main() {
	const GOMAXPROCS = 1
	runtime.GOMAXPROCS(GOMAXPROCS)
	var wg sync.WaitGroup
	wg.Add(N)
	for i := 0; i < N; i++ {
		go func(i int) {
			defer wg.Done()
			fmt.Println(i)
		}(i)
	}
	wg.Wait()
}

  

25
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24html

package main

import (
	"fmt"
	"runtime"
	"sync"
)

const N = 26

func main() {
	const GOMAXPROCS = 1
	runtime.GOMAXPROCS(GOMAXPROCS)
	var wg sync.WaitGroup
	wg.Add(N)
	for i := 0; i < N; i++ {
		go func() {
			defer wg.Done()
			fmt.Println(i)
		}()
	}
	wg.Wait()
}

  

26
26
26
26
26
26
26
26
26
26
26
26
26
26
26
26
26
26
26
26
26
26
26
26
26
26併發

package main

import (
	"fmt"
	"runtime"
	"sync"
)

const N = 26

func main() {
	const GOMAXPROCS = 1
	runtime.GOMAXPROCS(GOMAXPROCS)
	var wg sync.WaitGroup
	wg.Add(4)
	for i := 0; i < N; i++ {
		go func(i int) {
			defer wg.Done()
			fmt.Println(i)
		}(i)
	}
	wg.Wait()
}

  

25
0
1
2函數

package main

import "fmt"

func main() {
	for i:=0; i<10; i++ {
		go func() {
			fmt.Println(i)
		}()
	}
}

  無任何打印post

 

package main

import (
    "fmt"
    "runtime"
    "sync"
)

const N = 26

func main() {
    const GOMAXPROCS = 1
    runtime.GOMAXPROCS(GOMAXPROCS)
    var wg sync.WaitGroup
    wg.Add(2 * N)
    for i := 0; i < N; i++ {
        go func(i int) {
            defer wg.Done()
            fmt.Printf("%c", 'a'+i)
        }(i)

        go func(i int) {
            defer wg.Done()
            fmt.Printf("%c", 'A'+i)
        }(i)
    }
    go func() {}()
    wg.Wait()
}

 

經過無緩衝的通道阻塞來實現控制goroutine的執行順序spa

unbuffered channel
無緩衝的通道
在接收前沒有能力保存任何值的通道
要求發送goroutine和接收goroutine同時準備好,才能完成發送和接收的操做
若是兩個goroutine沒有同時準備好,通道會致使先執行發送或接收操做的goroutine阻塞等待
這種對通道進行發送和接收的交互行爲自己就是同步的
其中任意一個操做都沒法離開另外一個操做單獨存在

Go基礎系列:指定goroutine的執行順序 - 駿馬金龍 - 博客園 https://www.cnblogs.com/f-ck-need-u/p/9994652.htmlcode

 

package main

import (
	"fmt"
	"time"
)

func A(a, b chan struct{}) {
	<-a
	fmt.Println("A()!")
	close(b)
}

func B(a, b chan struct{}) {
	<-a
	fmt.Println("B()!")
	close(b)
}
func C(a chan struct{}) {
	<-a
	fmt.Println("C()!")
}

func main() {
	/*
		unbuffered channel
		無緩衝的通道
		在接收前沒有能力保存任何值的通道
		要求發送goroutine和接收goroutine同時準備好,才能完成發送和接收的操做
		若是兩個goroutine沒有同時準備好,通道會致使先執行發送或接收操做的goroutine阻塞等待
		這種對通道進行發送和接收的交互行爲自己就是同步的
		其中任意一個操做都沒法離開另外一個操做單獨存在
	*/
	x := make(chan struct{})
	y := make(chan struct{})
	z := make(chan struct{})
	go C(z)
	go B(y, z)
	go C(z)
	go A(x, y)
	go C(z)
	close(x)
	// 給打印留時間
	time.Sleep(3 * time.Second)
}

A()!
B()!
C()!
C()!
C()!htm

 

 goroutine併發寫blog

package main

import (
	"math/rand"
	"sync"
)

const N = 10

func main() {
	m := make(map[int]int)
	wg := &sync.WaitGroup{}
	wg.Add(N)
	for i := 0; i < N; i++ {
		go func() {
			defer wg.Done()
			m[rand.Int()] = rand.Int()
		}()
	}
	wg.Wait()
	println(len(m))
}

  當N相對大時,好比10e4報錯遊戲

加鎖資源

同步訪問共享資源的方式之一

使用互斥鎖mutex

互斥鎖概念來自互斥(mutual excusion)概念

互斥鎖用於在代碼上建立一個臨界區,保證同一時間只有一個goroutine能夠執行這個臨界區代碼

《Go 語言實戰》

 

package main

import (
	"math/rand"
	"sync"
)

const N = 100000

func main() {
	m := make(map[int]int)
	wg := &sync.WaitGroup{}
	var mutex sync.Mutex
	wg.Add(N)
	for i := 0; i < N; i++ {
		go func() {
			defer wg.Done()
			mutex.Lock()
			m[rand.Int()] = rand.Int()
			mutex.Unlock()
		}()
	}
	wg.Wait()
	println(len(m))
}

 

用無緩衝的通道來模擬2個goroutine間的網球比賽

package main

import (
	"fmt"
	"math/rand"
	"sync"
	"time"
)

// 用來等待程序結束
var wg sync.WaitGroup

func init() {
	rand.Seed(time.Now().UnixNano())
}
func main() {
	// 建立一個無緩衝的通道
	court := make(chan int)
	// 計數加2,表示要等待2個goroutine
	wg.Add(2)
	// 啓動2個選手
	go player("A", court)
	go player("B", court)
	// 發球
	court <- 1
	// 等待遊戲結束
	wg.Wait()
}

// player模擬一個選手在打網球
func player(name string, court chan int) {
	// 在函數退出時調用Done來通知main函數工做已經完成
	defer wg.Done()

	for {
		// 等待球被擊打過來
		ball, ok := <-court
		if !ok {
			// 若是通道關閉,咱們就贏了
			fmt.Printf("Player %s Won\n", name)
			return
		}
		// 選隨機數,而後用這個數來判斷咱們是否丟球
		n := rand.Intn(100)
		if n%13 == 0 {
			fmt.Printf("Player %s Missed\n", name)
			close(court)
			return
		}
		// 顯示擊球數,並將擊球數加1
		fmt.Printf("Player %s Hit %d\n", name, ball)
		ball++

		//  將球打向對手
		court <- ball

	}
}

  

Player B Hit 1
Player A Hit 2
Player B Hit 3
Player A Hit 4
Player B Hit 5
Player A Hit 6
Player B Hit 7
Player A Hit 8
Player B Missed
Player A Won

 

 接力比賽

package main

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

var wg sync.WaitGroup

func main() {
	// 建立一個無緩衝的通道
	baton := make(chan int)

	// 爲最後一位跑步者將計數加1
	wg.Add(1)

	// 第一位跑步者持有接力棒
	go Runner(baton)

	// 開始比賽
	baton <- 1

	// 等待比賽結束
	wg.Wait()
}

// Runner 模擬接力比賽中的一位跑步者

func Runner(baton chan int) {
	var newRunner int
	// 等待接力棒
	runner := <-baton

	// 開始繞着跑道跑步
	fmt.Printf("Runner %d Running With Baton\n", runner)

	// 建立下一位跑步者
	if runner != 4 {
		newRunner = runner + 1
		fmt.Printf("Runner %d To The Line\n", newRunner)
		go Runner(baton)
	}

	// 圍繞跑道跑
	time.Sleep(100 * time.Millisecond)

	// 比賽結束了嗎?
	if runner == 4 {
		fmt.Printf("Runner %d Finished, Race over\n", runner)
		wg.Done()
		return
	}

	// 將接力棒交給下一位跑步者
	fmt.Printf("Runner %d Exchange With Runner %d\n", runner, newRunner)

	baton <- newRunner

}

 

Runner 1 Running With Baton
Runner 2 To The Line
Runner 1 Exchange With Runner 2
Runner 2 Running With Baton
Runner 3 To The Line
Runner 2 Exchange With Runner 3
Runner 3 Running With Baton
Runner 4 To The Line
Runner 3 Exchange With Runner 4
Runner 4 Running With Baton
Runner 4 Finished, Race over

 

package main

import (
	"fmt"
	"math/rand"
	"sync"
	"time"
)

const (
	numberGorutines = 4  // 要使用的goroutine的數量
	taskLoad        = 10 //  要處理的工做的數量
)

var wg sync.WaitGroup

// init初始化包,Go語言運行時會在其餘代碼執行以前
// 優先執行這個函數
func init() {
	// 初始化隨機數種子
	rand.Seed(time.Now().Unix())
}

func main() {
	//  建立一個有緩衝的通道來管理工做
	tasks := make(chan string, taskLoad)

	// 啓動goroutine來處理工做
	wg.Add(numberGorutines)
	for gr := 1; gr <= numberGorutines; gr++ {
		go worker(tasks, gr)
	}

	// 增長一組要完成的工做
	for post := 1; post <= taskLoad; post++ {
		tasks <- fmt.Sprintf("Task : %d", post)
	}

	// 當全部工做都處理完時關閉通道
	// 以便全部goroutine退出
	close(tasks)

	// 等待全部工做完成
	wg.Wait()

}

// worker做爲goroutine啓動來處理
// 從有緩衝的通道傳入的工做

func worker(tasks chan string, worker int) {
	// 通知函數已經返回
	defer wg.Done()

	for {
		// 等待分配工做
		task, ok := <-tasks
		if !ok {
			// 這意味着通道已經空了,而且已被關閉
			fmt.Printf("Worker: %d : Shutting Down\n", worker)
			return
		}
		// 顯示咱們開始工做了
		fmt.Printf("Worker: %d : Started %s\n", worker, task)

		// 隨機等一段時間來模擬工做
		sleep := rand.Int63n(100)
		time.Sleep(time.Duration(sleep) * time.Millisecond)

		// 顯示咱們完成了工做
		fmt.Printf("Worker: %d : Completed %s \n", worker, task)
	}
}

 

Worker: 4 : Started Task : 1
Worker: 1 : Started Task : 2
Worker: 2 : Started Task : 3
Worker: 3 : Started Task : 4
Worker: 2 : Completed Task : 3
Worker: 2 : Started Task : 5
Worker: 2 : Completed Task : 5
Worker: 2 : Started Task : 6
Worker: 2 : Completed Task : 6
Worker: 2 : Started Task : 7
Worker: 4 : Completed Task : 1
Worker: 4 : Started Task : 8
Worker: 2 : Completed Task : 7
Worker: 2 : Started Task : 9
Worker: 4 : Completed Task : 8
Worker: 4 : Started Task : 10
Worker: 1 : Completed Task : 2
Worker: 1 : Shutting Down
Worker: 2 : Completed Task : 9
Worker: 2 : Shutting Down
Worker: 3 : Completed Task : 4
Worker: 3 : Shutting Down
Worker: 4 : Completed Task : 10
Worker: 4 : Shutting Down

 

可以從已經關閉的通道接收數據這一點很是重要,由於這容許通道關閉後依舊可以取出其中緩衝的所有值,而不會有數據丟失。從一個已經關閉且沒有數據的通道里獲取數據,總會馬上返回,並返回一個通道類型的零值。
相關文章
相關標籤/搜索