go协程之间的执行顺序以及和主协程的执行顺序

go协程之间的执行顺序以及和主协程的执行顺序,第1张

例1 
package main

var a string

func f() {
    print(a)
}

func hello() {
    a = "hello, world"
    go f()
}

func main() {
    hello()
}

输出:

不确定

可能调用hello函数的某一时刻打印;可能hello函数执行完成后打印;可能不打印"hello world"

原因:

执行go f()语句创建Goroutine和hello函数是在同一个Goroutine中执行, 根据语句的书写顺序可以确定Goroutine的创建发生在hello函数返回之前, 但是新创建Goroutine对应的f()的执行事件和hello函数返回的事件则是不可排序的,也就是并发的。

故不确定f()执行打印语句和hello函数返回谁先谁后,所以可能在hello函数返回之前或返回之后打印;如果hello函数返回了且main函数也退出了,那么程序就退出了,打印语句不会再执行。

例2
package main

import (
	"fmt"
)

func main() {
	ch := make(chan int)
	fmt.Println(<-ch)
	go func(){
	    ch <- 1
	}()
}

输出:

原因:

main函数执行到第9行时,要从通道ch取值,此时main协程会被阻塞,等待向通道存值的 *** 作;而在main协程中程序是顺序执行的,如果第9行一直阻塞着,下面的go协程也不可能执行,main协程也永远等不来向通道存值的 *** 作;运行时会检查主协程是否在等待,如果是则判定为死锁。

例3
package main
import (
    "time"
)

func main() {
	ch1 := make(chan string)
	ch2 := make(chan string)
	go func() {
		ch2 <- "ch2 value"
		ch1 <- "ch1 value"
	}()

	go func() {
		<- ch1
		<- ch2
	}()

	time.Sleep(time.Second * 2)
    print("main done")
}

输出:

main done

原因:

main函数顺序执行到第8行时,此时摆在面前的有3个协程:2个go协程和main协程,它们的执行是并发的,谁都有可能先发生。main协程第19行休眠2s,保证main协程不会太快执行完,因为如果main函数执行结束,程序会退出,不会等待其他非main协程结束;不管这2个go协程哪个先执行,都会导致2个go协程都被阻塞等待;此时main协程休眠完,执行完第20行,程序退出。尽管2个go协程互相阻塞,但和main协程无关,故不会报死锁错误。

例4
package main

var done = make(chan bool, 1)
var msg string

func aGoroutine() {
    msg = "hello, world"
    <-done
}

func main() {
    go aGoroutine()
    done <- true
    println(msg)
}

输出:

2种可能:

打印为空或打印"hello world"

原因:

通道done是带缓冲的,main协程done <- true接收 *** 作将不会被后台协程的<-done接收 *** 作阻塞,若第14行打印语句早于aGoroutine协程对msg赋值执行,则打印为空;若msg赋值早于打印语句执行,则打印"hello world"。

参考:

https://learnku.com/docs/the-way-to-go/142-covariance-channel/3686

https://juejin.cn/post/6844903881843933197

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/996154.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-21
下一篇 2022-05-21

发表评论

登录后才能评论

评论列表(0条)

保存