zoukankan      html  css  js  c++  java
  • 8.14GO之条件语句

    8.14GO之条件语句

    Go语言条件语句

    一些大题的和类C语言无异,但是:

    • Go 没有三目运算符,所以不支持 ?: 形式的条件判断。

    相当于Java中的:

        public boolean isEmpty(){
           return size == 0 ? true : false;
      }

    Go之条件语句

    语句描述
    [if 语句] if 语句 由一个布尔表达式后紧跟一个或多个语句组成。
    [if...else 语句] if 语句 后可以使用可选的 else 语句, else 语句中的表达式在布尔表达式为 false 时执行。
    [if 嵌套语句] 你可以在 ifelse if 语句中嵌入一个或多个 ifelse if 语句。
    [switch 语句] switch 语句用于基于不同条件执行不同动作。
    [select 语句] select 语句类似于 switch 语句,但是select会随机执行一个可运行的case。如果没有case可运行,它将阻塞,直到有case可运行。
    Go条件语句之select语句

    声明格式:

    select {
       case communication clause :
          statement(s);      
       case communication clause :
          statement(s);
       /* 你可以定义任意数量的 case */
       default : /* 可选 */
          statement(s);
    }

    简单理解:

    • chan关键字定义了goroutine中的管道通信,一个goroutine可以和另一个goroutine进行通信。

    什么是goroutine

    goroutine是Go中最基本的执行单元。

    特点:

    • 每一个Go程序至少有一个Goroutine:主Goroutine。

    线程(Thread)

    特点:

    • 轻量级进程(Lightweight Process,LWP)

    • 程序执行流的最小单元

    一个标准线程的组成:

    • 线程ID

    • 当前指令指针(PC)

    • 寄存器集合

    一个线程的特点:

    • 是进程中的一个实体,是被系统独立调度和分派的基本单位

    • 多线程之间 共享堆,不共享栈。 线程切换由操作系统调度

    协程(coroutine)

    特点:

    • 称微线程与子例程(或者称为函数)

    • 是一种程序组件。相对子例程而言,协程更为一般和灵活,但在实践中使用没有子例程那样广泛

    一个协程的特点:

    • 多协程之间 共享堆,不共享栈

    线程与协程的不同点
    • 线程切换由操作系统调度

    • 协程的切换由程序员在代码中显示控制

    好处:

    • 避免了上下文切换的额外耗费

    • 兼顾了多线程的优点

    • 简化了高并发程序的复杂

    Goroutine--->需要深入了解、实践。目前还不是很明白。

    特点:

    • 协程不是并发的,而Goroutine支持并发的。

    • Goroutine可以理解为一种Go语言的协程。

    • 同时它可以运行在一个或多个线程上。-

    • goroutine支持协程之间的互相通信--->使用关键字chanchannel的简写

    channel的特点

    特点:

    • 一个 channels 是一个通信机制,可以让一个 goroutine 通过它给另一个 goroutine 发送值信息。

    • 每个 channel 都有一个特殊的类型,也就是 channels 可发送数据的类型。一个可以发送 int 类型数据的 channel 一般写为 chan int。

    和多线程ThreadLocal不一样的地方:

    ThreadLocal:

    • ThreadLocal能够放一个线程级别的变量

    • 本身能够被多个线程共享使用,又能达到线程安全的目的

    • 在多线程环境下保证成员变量的安全

    ThreadLocal相当于一个共享的内存区域,供多个线程共享堆的数据。线程只能共享堆的数据,不能共享栈的数据。

    Goroutine:

    • Go语言提倡使用通信的方法代替共享内存

    • 资源需要在 goroutine 之间共享时,通道在 goroutine 之间架起了一个管道,并提供了确保同步交换数据的机制。

    • 因为go是强类型语言,所以声明通道时,需要指定将要被共享的数据的类型。

    • 可以通过通道共享goroutine当中的内置类型、命名类型、结构类型和引用类型的值或者指针。

    可以说goroutine实现了协程之间的栈的数据的共享。

    通信的方法就是使用通道(channel),如下图所示:

    channel 是一种队列一样的结构。

    channel规则的特点:

    • 可以向一个goroutine建立多个channel通道

    • 同时只能有一个 goroutine 访问通道进行发送和获取数据。

    • goroutine遵循先入先出(First In First Out)的规则,保证收发数据的顺序。--->和栈空间的规则一样

    通道的使用方式
    • 声明通道

    • 创建通道

    • 使用通道发送数据


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

    声明通道类型格式:
    var 通道变量 chan 通道类型
    • 通道类型:通道内的数据类型。

    • 通道变量:保存通道的变量。--->通道本身也是一个变量,

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

    创建通道格式--->通道本身是引用类型,需要使用make进行创建
    通道实例 := make(chan 数据类型)
    • 数据类型:通道内传输的元素类型。

    • 通道实例:通过make创建的通道句柄。--->可以理解为在堆中开辟一个空间

    示例:

    package main

    import (
    "expvar"
    "fmt"
    )

    func main() {
    ch1 := make(chan int) //创建一个整数类型的通道
    ch2 := make(chan interface{}) //创建一个空接口类型的通道, 可以存放任意格式

    type Equip struct {
    expvar.Var
    }

    ch2 := make(chan *Equip)
    fmt.Println()
    }
    使用通道发送数据--->操作符:<-

    通道发送苏剧的格式:

    通道变量 <- 
    • 通道变量:通过make创建好的通道实例。--->这是一个堆里面的实际对象,对应到一个堆的地址

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

    示例:

    package main

    func main() {
    ch1 := make(chan int) //创建一个整数类型的通道
    ch2 := make(chan interface{}) //创建一个空接口类型的通道, 可以存放任意格式

    ch1 <- 1
    ch2 <- 3.1415926
    ch2 <- "Hello,World!"
    }
    使用通道接受数据--->操作符:<-

    通道接收数据的特征:

    • 通道的收发操作在不同的两个 goroutine 间进行。

      • 通道的数据在没有接收方处理时,数据发送方会持续阻塞,因此通道的接收必定在另外一个 goroutine 中进行。

    • 接收将持续阻塞直到发送方发送数据。

      • 接收方接收时,通道中没有发送方发送数据,接收方也会发生阻塞,直到发送方发送数据为止。

    • 每次接收一个元素。

      • 通道一次只能接收一个数据元素。

    概括:

    • 通过通道传送数据需要两个goroutine

    • 发送没有接收会阻塞goroutine,接收没有发送数据也会阻塞goroutine--->非常类似Java多线程中的线程通信模型

    • 可以创建非阻塞接收数据的类型

    接收数据的四种写法:

    1. 阻塞式接收数据
    2. 非阻塞式接收数据
    3. 接收任意数据,忽略接收的数据
    4. 循环接收

    阻塞式接收数据:

    data <- ch

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

    非阻塞式接收数据:

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

    • ok:表示是否接收到数据。

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

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

    <- ch

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

    通过通道在 goroutine 间阻塞收发实现并发同步。

    使用通道做同步并发的实例:

    package main

    import "fmt"

    func main() {
    //构建一个通道
    ch := make(chan int)

    //开启一个并发匿名函数
    go func() {
    fmt.Println("start goroutine")

    //通过通道通知main的goroutine
    ch <- 0
    fmt.Println("exit goroutine")
    }()

    fmt.Println("wait goroutine")

    //等待匿名goroutine
    <- ch

    fmt.Println("all done")
    }

    可以看到执行顺序:

    1. 先执行main中的构建和通道接收的内容。到<- ch此时goroutine进入阻塞状态

    2. 接着等到go func()当中的goroutine运行向通道ch中添加值

    3. 最后main中收到了值以后解除阻塞状态,往下运行

    所以输出顺序是:

    wait goroutine
    start goroutine
    exit goroutine
    all done

    代码说明:

    ch := make(chan int) //构建一个同步用的通道。
    go func() //开启一个匿名函数的并发。
    ch <- 0 //匿名 goroutine 即将结束时,通过通道通知 main 的 goroutine,这一句会一直阻塞直到 main 的 goroutine 接收为止。
    <- ch //开启 goroutine 后,马上通过通道等待匿名 goroutine 结束。

    虽然是并发运行但是通过阻塞的方式还是可以实现数据的安全、准确性

    循环接收:

    借用 for range 语句进行多个元素的接收操作

    for data := range ch {
    }

    特点:

    • 通道 ch 可以进行遍历--->遍历的结果就是接收到的数据。数据类型就是通道的数据类型。

    • 通过 for 遍历获得的变量只有一个,即 data。

    有点类似增强for循环

    示例:

    package main

    import (
    "fmt"
    "time"
    )

    func main() {
    //构建一个通道
    chNo2 := make(chan int)

    //开启一个并发匿名函数
    go func() {

    //for循环,从3->0
    for i := 3; i > 0; i-- {
    //每次将循环的值发送到main的goroutine中
    chNo2 <- i

    //每次发送完时等待
    time.Sleep(time.Second)
    }
    }()

    //遍历接收通道的数据
    for data := range chNo2 {
    //打印通道数据
    fmt.Println(data)

    //当遇到数据0时,退出接收循环
    if data == 0 {
    break
    }
    }
    }

    代码说明:

    ch := make(chan int)
    //通过 make 生成一个整型元素的通道。
    go func() //将匿名函数并发执行。
    ch <- i //将 3 到 0 之间的数值依次发送到通道 ch 中。
    for data := range ch //使用 for 从通道中接收数据。

    本篇内容参考:

    Go语言通道(chan)——goroutine之间通信的管道

    Go goroutine理解 - SegmentFault 思否

    go的并发实现原理在后续学习到以后会深入的记录学习过程。

  • 相关阅读:
    《你的灯还亮着吗》读后感1
    找"1"
    阅读计划---《梦断代码》3
    阅读计划---《梦断代码》2
    个人工作总结(10)
    个人工作总结(9)
    个人工作总结(8)
    个人工作总结(7)
    学习进度条
    个人工作总结(6)
  • 原文地址:https://www.cnblogs.com/JunkingBoy/p/15147821.html
Copyright © 2011-2022 走看看