go-并发

    xiaoxiao2025-06-24  6

    以下的程序,我们串行地去执行两次loop函数:

    package main import "fmt" func loop() { for i := 0; i < 10; i++ { fmt.Printf("%d ", i) } } func main() { loop() loop() }

    输出如下:

    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9

    下面我们把一个loop放在一个goroutine里跑,我们可以使用关键字go来定义并启动一个goroutine:

    package main import "fmt" func loop() { for i := 0; i < 10; i++ { fmt.Printf("%d ", i) } } func main() { go loop() loop() }

    原来,在goroutine还没来得及跑loop的时候,主函数已经退出了。 main函数退出地太快了,我们要想办法阻止它过早地退出,一个办法是让main等待一下:

    package main import "fmt" import "time" func loop() { for i := 0; i < 10; i++ { fmt.Printf("%d ", i) } } func main() { go loop() loop() time.Sleep(2 * time.Second) // 停顿一秒 } E:/golang/go/bin/go.exe build -i [E:/golang/go_project/src] 成功: 进程退出代码 0. E:/golang/go_project/src/src.exe [E:/golang/go_project/src] 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 成功: 进程退出代码 0.

    可是采用等待的办法并不好,如果goroutine在结束的时候,告诉下主线说“Hey, 我要跑完了!”就好了, 即所谓阻塞主线的办法 信道是什么?简单说,是goroutine之间互相通讯的东西, 用来goroutine之间发消息和接收消息。其实,就是在做goroutine之间的内存共享。 使用make来建立一个信道: 默认的,信道的存消息和取消息都是阻塞的 (叫做无缓冲的信道)。 也就是说, 无缓冲的信道在取消息和存消息的时候都会挂起当前的goroutine,除非另一端已经准备好。

    package main import "fmt" func main() { // var channel chan int = make(chan int) var messages chan string = make(chan string) go func(message string) { messages <- message // 存消息 }("Ping!") fmt.Println(<-messages) // 取消息 } 输出: Ping!

    从messages取数据,如果messages中还没放数据,那就挂起main线,进入阻塞状态,直到loop函数中放数据为止,向messages中加数据,如果没有其他goroutine来取走这个数据,那么挂起foo, 直到main函数把0这个数据拿走

    死锁

    package main func main() { ch := make(chan int) <-ch // 阻塞main goroutine, 信道c被锁 }

    所有的线程或进程都在等待资源的释放。如上的程序中, 只有一个goroutine, 所以当你向里面加数据或者存数据的话,都会锁死信道, 并且阻塞当前 goroutine, 也就是所有的goroutine(其实就main线一个)都在等待信道的开放(没人拿走数据信道是不会开放的),也就是死锁咯

    总结来看,为什么会死锁?非缓冲信道上如果发生了流入无流出,或者流出无流入,也就导致了死锁。或者这样理解 Go启动的所有goroutine里的非缓冲信道一定要一个线里存数据,一个线里取数据,要成对才行

    无缓冲信道的数据进出顺序

    package main import "fmt" var ch chan int = make(chan int) func foo(id int) { //id: 这个routine的标号 ch <- id } func main() { // 开启5个routine for i := 0; i < 5; i++ { go foo(i) } // 取出信道中的数据 for i := 0; i < 5; i++ { fmt.Print(<-ch) } } 输出: 03124(随机输出01234个数字)

    我们开了5个goroutine,然后又依次取数据。其实整个的执行过程细分的话,而宏观上我们看到的即 无缓冲信道的数据是先到先出,但是 无缓冲信道并不存储数据,只负责数据的流通

    缓冲信道

    缓冲这个词意思是,缓冲信道不仅可以流通数据,还可以缓存数据。它是有容量的,存入一个数据的话 , 可以先放在信道里,不必阻塞当前线而等待该数据取走。

    package main var a string var c = make(chan int, 10) func f() { a = "hello, world" c <- 0 } func main() { go f() <-c print(a) } 输出: hello, world

    无缓冲信道

    package main var a string var c = make(chan int) func f() { a = "hello, world" <-c } func main() { go f() c <- 0 print(a) } 输出: hello, world
    转载请注明原文地址: https://ju.6miu.com/read-1300289.html
    最新回复(0)