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

 

可以从已经关闭的通道接收数据这一点很是重要,由于这容许通道关闭后依旧可以取出其中缓冲的所有值,而不会有数据丢失。从一个已经关闭且没有数据的通道里获取数据,总会马上返回,并返回一个通道类型的零值。
相关文章
相关标签/搜索