Golang语言之Channel通信,并发编程

1.Go语言特点

Go是一种静态强类型的开源语言,诞生于2009年,是非常年轻的一门语言其主要目标时“兼具Python等动态语言的开发速度,同时又具备想C语言C++的安全性与应能”,其中最大的特点(优势)就是并发编程

不同于大多编程语言的多线程,golang的并发执行单元是一种称之为goroutine的携程,由于绝大部分语言在其共享数据时会用到并发锁,再加上GC,其执行效率多多少少会受到影响,golang的并发编程简单。

  1. channel的使用——并发编程
 ch := make(chan int)

这种是无缓冲的channel,一旦goroutine向管道内发送数据,那么当前的goroutine会被阻塞,直到其他的携程消费了管道里的数据才能正常运行。

ch := make(chan int, 2)

这种是有缓存的管道,与上面相比增加以一个uint型参数表示缓存容量,表示可并发执行的最大数量,只要当前channel类的元素总数不大于可缓冲容量,则当前的协程就不会阻塞住。

图1.png

*注意:管道的出入方式与出弹栈有所不同,前者时先入先出而后者是先入后出(如下图)

图2.png

2.1多个goroutine协同

多个写成最后汇总至结果

package main

func main() {

    println("start main")

ch := make(chan int)

    varresult int

    go func() {

        println("come into goroutine1")

        varr int

        fori := 1;i <= 10; i++ {

r += i

        }

ch <- r

    }()

    go func() {

        println("come into goroutine2")

        varr int = 1

        fori := 1;i <= 10; i++ {

r *= i

        }

ch <- r

    }()

    go func() {

        println("come into goroutine3")

ch <- 11

    }()

    fori := 0;i < 3; i++ {

result += <-ch

    }

    close(ch)

    println("result is:", result)

    println("end main")}

其执行结果如下:

start main

come into goroutine3

come into goroutine2

come into goroutine1

result is: 3628866

end main

*从执行效率看go语言的并发效率要比Java和C的高出不少,在上述代码中三个go func并发执行,并且互不干涉(这里还没有涉及到互斥锁)最终输出的顺序还没有混乱。

2.2 channel底层原理

再往下深究至源码 channel返回类型hchan结构体,如下图

type hchan struct {

 qcount   uint   // channel 里的元素计数

 dataqsiz uint   // 可以缓冲的数量,如 ch := make(chan int, 10)。 此处的 10 即 dataqsiz

 elemsize uint16 // 要发送或接收的数据类型大小

 buf      unsafe.Pointer // 当 channel 设置了缓冲数量时,该 buf 指向一个存储缓冲数据的区域,该区域是一个循环队列的数据结构

 closed   uint32 // 关闭状态

 sendx    uint  // 当 channel 设置了缓冲数量时,数据区域即循环队列此时已发送数据的索引位置

 recvx    uint  // 当 channel 设置了缓冲数量时,数据区域即循环队列此时已接收数据的索引位置

 recvq    waitq // 想读取数据但又被阻塞住的 goroutine 队列

 sendq    waitq // 想发送数据但又被阻塞住的 goroutine 队列

 lock mutex

 ...

}

该结构体中详细定义了管道内的各种操作 channel再进行数据写入写出时会有缓冲设置以及无缓冲的设置其异同如下

无缓冲channel

先写再读

图3.png

由于 channel 是无缓冲的,所以 G1 暂时被挂起在队列里(由结构体中sendq字段接收),然后 G1 调用了 gopark字段 休眠了起来。接着,又有协程来channel读取数据了:

先读再写

如下图:

图4.png

此时G1被暂时性挂起,等待进入channel

G2在进入管道时发现前面有协程存在,于是给G1发讯息,等待下次被调。

如下图:

图5.png

作者:吴韵寰

猜你喜欢

转载自blog.csdn.net/ekcchina/article/details/130056024