Go语言的协程信道(go语言协程和线程的区别)

网友投稿 1340 2022-08-19

Go语言的协程和信道(go语言协程和线程的区别)

Go语言的协程和信道(go语言协程和线程的区别)

学习Go语言,就不得不学习协程(Goroutines)和信道(channels),正是因为有了协程和信道的机制,才使得Go语言对高并发的天然支持,下面就让我们对协程和信道的使用一探究竟吧!

协程(Goroutines)

1. 什么是协程

Goroutines are functions or methods that run concurrently with other functions or methods. Goroutines can be thought of as light weight threads. The cost of creating a Goroutine is tiny when compared to a thread. Hence its common for Go applications to have thousands of Goroutines running concurrently.

从协程的概念中我们可以看出协程是一种函数或者一种方法,它们可以并发的运行。协程可以被看作是轻量级的线程,但是与线程相比,创建一个协程的成本很低,因此在Go语言开发的应用中,经常可以看到成千上万的协程并发执行。

2. 如何使用协程

协程的使用十分简单,只需要在调用函数或者方法时,在前面加上关键字go,就启动了一个新的Go协程(注意:主函数无需加关键字go,它默认运行在一个Go协程上,被称为Go主协程Main Goroutine),下面我们就通过示例演示如何创建一个协程:

package main

import (

"fmt"

)

func hello() {

fmt.Println("this is hello function")

}

func main() {

go hello()

fmt.Println("this is main function")

}

上述代码中,我们在调用hello()函数时,对其加了go关键字,因此就创建了一个协程,是不是超级简单啊!是的,就是这么简单,但是当你运行程序之后,你会发现代码只输出了"this is main function",这是为什么呢?

这是因为Go语言的协程有两个重要的性质:

启动一个新的协程时,协程的调用会立即返回。与函数不同,程序控制不会去等待 Go 协程执行完毕。在调用 Go 协程之后,程序控制会立即返回到代码的下一行,忽略该协程的任何返回值。

如果希望运行其他 Go 协程,Go 主协程必须继续运行着。如果 Go 主协程终止,则程序终止,于是其他 Go 协程也不会继续运行。

这时你就会想,那么怎样才能等待协程执行完毕呢,各位大佬别急,下面就该轮到信道登场了。所以,抛开信道谈协程,就像是抛开现实谈理想,缺少脚踏实地做支撑,终究是竹篮打水一场空。

信道(Channels)

1. 什么是信道

Channels can be thought as pipes using which Goroutines communicate. Similar to how water flows from one end to another in a pipe, data can be sent from one end and received from the another end using channels.

信道可以看作是协程之间通信的管道,就好像管道中的水可以从一头流动到另一头一样,通过信道,数据也可以从一端发送,从另一端接收。

2. 信道使用的注意点

类型:所有信道都关联了一个类型。信道只能运输这种类型的数据,而运输其他类型的数据都是非法的;

定义:需要像对 map 和切片所做的那样,用 make 来定义信道;

零值:信道的零值为 nil;

发送和接收:信道旁的箭头方向指定了是发送数据还是接收数据,如果箭头指向信道,则是把数据写入信道,反之则为从信道读取数据;

阻塞:信道的发送与接收默认是阻塞的,即当把数据发送到信道时,程序控制会在发送数据的语句处发生阻塞,直到有其它Go协程从信道读取到数据,才会解除阻塞。与此类似,当读取信道的数据时,如果没有其它的协程把数据写入到这个信道,那么读取过程就会一直阻塞着;

单向信道:我们可以通过箭头的指向来创建单向信道,这种信道只能发送或者接收数据。一个双向信道可以转换成唯送信道(Send Only)或者唯收信道(Receive Only),但是反过来不行;

死锁:(对于双向信道)当Go协程给一个信道发送数据时,需要有其他Go协程来接收数据。如果没有的话,程序就会在运行时触发panic,形成死锁。同理,当有Go协程等着从一个信道接收数据时,必须要有其他的 Go协程向该信道写入数据,要不然程序就会触发 panic;

关闭信道:使用close关闭信道;

遍历信道:可以使用for  range来遍历信道;

缓冲信道:在用make函数创建信道时,我们可以传递一个参数来指定缓冲的大小,如ch := make(chan type, capacity),默认不填的情况下capacity的值为0,也就是所谓的无缓冲信道。对于缓冲信道,只在缓冲已满的情况,才会阻塞向缓冲信道(Buffered Channel)发送数据。同样,只有在缓冲为空的时候,才会阻塞从缓冲信道接收数据。

3. 信道的使用示例:

package main

import (

"fmt"

"time"

)

func write(ch chan int) {

for i := 0; i < 5; i++ {

ch <- i

fmt.Println("successfully wrote", i, "to ch")

}

close(ch)

}

func main() {

ch := make(chan int, 2)

go write(ch)

time.Sleep(2 * time.Second)

for v := range ch {

fmt.Println("read value", v,"from ch")

time.Sleep(2 * time.Second)

}

}

上面的代码中,我们创建了一个容量为2的缓冲信道,并将其作为参数传给了write协程。write 协程有一个 for 循环,依次向信道 ch 写入 0~4。而缓冲信道的容量为 2,因此 write 协程里立即会向 ch 写入 0 和 1,接下来发生阻塞,直到 ch 内的值被读取。

主协程在休眠了两秒后,使用 for range 循环读取信道ch的值并打印,打印完之后又休眠两秒。该过程会一直进行,直到信道读取完所有的值,并在 write 协程中关闭信道。最终代码执行输出如下:

successfully wrote 0 to ch

successfully wrote 1 to ch

read value 0 from ch

successfully wrote 2 to ch

read value 1 from ch

successfully wrote 3 to ch

read value 2 from ch

successfully wrote 4 to ch

read value 3 from ch

read value 4 from ch

这就是Go语言协程和信道的简单使用方法,后面的文章中我们会学习它们的一些更加复杂的用法。

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:Win10新添实用功能,你发现了吗?(win10的新功能介绍)
下一篇:Go语言微服务实战之初识微服务(Go 微服务实战 38 讲)
相关文章

 发表评论

暂时没有评论,来抢沙发吧~