zoukankan      html  css  js  c++  java
  • 并发——轻量级线程,通道,单向通道

    1、轻量级线程

    goroutine 是 Go语言中的轻量级线程实现,由 Go 运行时(runtime)管理。Go 程序会智能地将 goroutine 中的任务合理地分配给每个 CPU。
    Go 程序从 main 包的 main() 函数开始,在程序启动时,Go 程序就会为 main() 函数创建一个默认的 goroutine。

    1)使用普通函数创建goroutine

    Go 程序中使用 go 关键字为一个函数创建一个 goroutine。一个函数可以被创建多个 goroutine,一个 goroutine 必定对应一个函数。

    为一个普通函数创建 goroutine 的写法如下:

    go 函数名( 参数列表 )

    • 函数名:要调用的函数名。
    • 参数列表:调用函数需要传入的参数。

    使用 go 关键字创建 goroutine 时,被调用函数的返回值会被忽略。

    如果需要在 goroutine 中返回数据,通过通道把数据从 goroutine 中作为返回值传出。

    2)使用匿名函数创建goroutine

    go 关键字后也可以为匿名函数或闭包启动 goroutine。

    使用匿名函数或闭包创建 goroutine 时,除了将函数定义部分写在 go 的后面之外,还需要加上匿名函数的调用参数,格式如下:

    go func( 参数列表 ){
        函数体
    }( 调用参数列表 )

    其中:

    • 参数列表:函数体内的参数变量列表。
    • 函数体:匿名函数的代码。
    • 调用参数列表:启动 goroutine 时,需要向匿名函数传递的调用参数。

    2、通道

    Go语言提倡使用通信的方法代替共享内存,当一个资源需要在 goroutine 之间共享时,通道在 goroutine 之间架起了一个管道,并提供了确保同步交换数据的机制。声明通道时,需要指定将要被共享的数据的类型。可以通过通道共享内置类型、命名类型、结构类型和引用类型的值或者指针。

    Go语言中的通道(channel)是一种特殊的类型。在任何时候,同时只能有一个 goroutine 访问通道进行发送和获取数据。goroutine 间通过通道就可以通信。
    通道像一个传送带或者队列,总是遵循先入先出的规则,保证收发数据的顺序。

    1)声明通道类型

    通道本身需要一个类型进行修饰,就像切片类型需要标识元素类型。通道的元素类型就是在其内部传输的数据类型,声明如下:

    var 通道变量 chan 通道类型

    • 通道类型:通道内的数据类型。
    • 通道变量:保存通道的变量。

    chan 类型的空值是 nil,声明后需要配合 make 后才能使用。

    2)创建通道

    通道是引用类型,需要使用 make 进行创建,格式如下:

    通道实例 := make(chan 数据类型)

    • 数据类型:通道内传输的元素类型。
    • 通道实例:通过make创建的通道句柄。

    3)使用通道发送数据

    通道的发送使用特殊的操作符<-,将数据通过通道发送的格式为:通道变量 <- 值

    • 通道变量:通过make创建好的通道实例。
    • 值:可以是变量、常量、表达式或者函数返回值等。值的类型必须与ch通道的元素类型一致。

    把数据往通道中发送时,如果接收方一直都没有接收,那么发送操作将持续阻塞。

    4)使用通道接收数据

    通道接收同样使用<-操作符,通道接收有如下特性:

    •  通道的收发操作在不同的两个 goroutine 间进行。由于通道的数据在没有接收方处理时,数据发送方会持续阻塞,因此通道的接收必定在另外一个 goroutine 中进行。
    • 接收将持续阻塞直到发送方发送数据。如果接收方接收时,通道中没有发送方发送数据,接收方也会发生阻塞,直到发送方发送数据为止。
    • 每次接收一个元素。通道一次只能接收一个数据元素。

    通道的数据接收的4种写法:

    阻塞接收数据:

    阻塞模式接收数据时,将接收变量作为<-操作符的左值,格式如下:

    data := <-ch

    执行该语句时将会阻塞,直到接收到数据并赋值给 data 变量。

    非阻塞接收数据:

    使用非阻塞方式从通道接收数据时,语句不会发生阻塞,格式如下:

    data, ok := <-ch

    • data:表示接收到的数据。未接收到数据时,data 为通道类型的零值。
    • ok:表示是否接收到数据。

    非阻塞的通道接收方法可能造成高的 CPU 占用,因此使用非常少。如果需要实现接收超时检测,可以配合 select 和计时器 channel 进行。

    接收任意数据,忽略接收的数据:

    阻塞接收数据后,忽略从通道返回的数据,格式如下:

    <-ch

    执行该语句时将会发生阻塞,直到接收到数据,但接收到的数据会被忽略。

    循环接收:

    通道的数据接收可以借用 for range 语句进行多个元素的接收操作,格式如下:

    for data := range ch {
    }

    通道 ch 是可以进行遍历的,遍历的结果就是接收到的数据。数据类型就是通道的数据类型。通过 for 遍历获得的变量只有一个,即上面例子中的 data。

    3、单向通道

    Go语言的类型系统提供了单方向的 channel 类型,顾名思义,单向 channel 只能用于发送或者接收数据。channel 本身必然是同时支持读写的,否则根本没法用。
    假如一个 channel 真的只能读,那么肯定只会是空的,因为你没机会往里面写数据。同理,如果一个 channel 只允许写,即使写进去了,也没有丝毫意义,因为没有机会读取里面的数据。所谓的单向 channel 概念,其实只是对 channel 的一种使用限制。

    1)单向通道的声明

    我们在将一个 channel 变量传递到一个函数时,可以通过将其指定为单向 channel 变量,从而限制该函数中可以对此 channel 的操作。

    单向 channel 变量的声明非常简单,只能发送的通道类型为chan<-,只能接收的通道类型为<-chan,格式如下:

    var 通道实例 chan<- 元素类型    // 只能发送通道
    var 通道实例 <-chan 元素类型    // 只能接收通道

    • 元素类型:通道包含的元素类型。
    • 通道实例:声明的通道变量。

    2)关闭channel

    关闭 channel 非常简单,直接使用 Go语言内置的 close() 函数即可:close(ch)

  • 相关阅读:
    spring揭密学习笔记(3)-spring ioc容器:Spring的IoC容器之BeanFactory
    spring揭密学习笔记(3)-spring ioc容器:掌管大局的IoC Service Provider
    spring揭密学习笔记(2)-spring ioc容器:IOC的基本概念
    spring揭密学习笔记(1) --spring的由来
    spring揭密学习笔记
    spring事务管理实现原理-源码-传播属性
    spring事务传播实现源码分析
    IDEA搭建Spring框架环境
    ScrollView滑动的监听
    Android对apk源代码的改动--反编译+源代码改动+又一次打包+签名【附HelloWorld的改动实例】
  • 原文地址:https://www.cnblogs.com/ACGame/p/12005757.html
Copyright © 2011-2022 走看看