浅析Go中Channel的各路用法

简介: 浅析Go中Channel的各路用法

Go语言基础四


今天我们要来学习if语句,也就是大家口中的判断语句,我们首先来看一下if语句的定义


if定义


条件语句需要开发者通过指定一个或多个条件并通过测试条件是否为 true 来决定是否执行指定语句,并在条件为 false 的情况在执行另外的语句。相信读者看到这儿,也是云里雾里的感觉,我们怎么来表示truefalse呢?


单层if语法格式


• 可省略条件表达式括号。
• 持初始化语句,可定义代码块局部变量。 
• 代码块左 括号必须在条件表达式尾部。
    if 布尔表达式 {
    /* 在布尔表达式为 true 时执行 */
    }     

这里要为读者介绍的是,如果if后面的条件语句程序给出的数能够满足,则我们表示为true如果不能,则返回false

package main
import "fmt"
func main() {
   a := 3
   if a > 2 {
      fmt.Println("true")
   } else {
      fmt.Println("false")
   }
}

如代码块所示,这里我们定义了一个变量a的值为3,接下来是一个if判断,如果该数值>2,则调用函数打印输出true;否则返回false。换句话说,我们这里即是对a变量的一个判断,至于调用函数打印输出的内容,由读者自行决定


语法警告


在Go语法中,不支持三元操作符(三目运算符) a > b ? a : b,如果读者对三元运算符较感兴趣,可以移步至java了解三元运算符(其本质上也是一种if判断形式

package main
import "fmt"
func main() {
   /* 定义局部变量 */
   var a int = 10
   /* 使用 if 语句判断布尔表达式 */
   if a < 20 {
       /* 如果条件为 true 则执行以下语句 */
       fmt.Printf("a 小于 20\n" )
   }
   fmt.Printf("a 的值为 : %d\n", a)
}  

上方是关于if判断的一个小练习,读者自行体会即可;if 在布尔表达式为 true 时,其后紧跟的语句块执行,如果为 false 则执行 else 语句块。

package main
import "fmt"
func main() {
   /* 局部变量定义 */
   var a int = 100
   /* 判断布尔表达式 */
   if a < 20 {
       /* 如果条件为 true 则执行以下语句 */
       fmt.Printf("a 小于 20\n" )
   } else {
       /* 如果条件为 false 则执行以下语句 */
       fmt.Printf("a 不小于 20\n" )
   }
   fmt.Printf("a 的值为 : %d\n", a)
}

Go语言中,if语句也支持嵌套处理,即可以实现多重if判断以达到程序想要的结果


多层if语法格式


if 布尔表达式 1 {
   /* 在布尔表达式 1 为 true 时执行 */
   if 布尔表达式 2 {
      /* 在布尔表达式 2 为 true 时执行 */
   }
}   
package main
import "fmt"
func main() {
   /* 定义局部变量 */
   var a int = 100
   var b int = 200
   /* 判断条件 */
   if a == 100 {
       /* if 条件语句为 true 执行 */
       if b == 200 {
          /* if 条件语句为 true 执行 */
          fmt.Printf("a 的值为 100 , b 的值为 200\n" )
       }
   }
   fmt.Printf("a 值为 : %d\n", a )
   fmt.Printf("b 值为 : %d\n", b )
}     

如上图所示,我们在if语句里面嵌套了另一个if语句,即是在默认a == 100的情况下写出对变量b的值的判断,最终调用函数打印输出a和b的值

有时候我们多个变量匹配同一个值,就会用到Switch语句


Switch定义


switch 语句用于基于不同条件执行不同动作,每一个 case 分支都是唯一的,从上直下逐一测试,直到匹配为止。Golang switch 分支表达式可以是任意类型,不限于常量。可省略 break,默认自动终止。


Switch语法格式


switch var1 {
    case val1:
        ...
    case val2:
        ...
    default:
        ...
}
package main
import "fmt"
func main() {
   /* 定义局部变量 */
   var grade string = "B"
   var marks int = 90
   switch marks {
      case 90: grade = "A"
      case 80: grade = "B"
      case 50,60,70 : grade = "C"
      default: grade = "D"  
   }
   switch {
      case grade == "A" :
         fmt.Printf("优秀!\n" )     
      case grade == "B", grade == "C" :
         fmt.Printf("良好\n" )      
      case grade == "D" :
         fmt.Printf("及格\n" )      
      case grade == "F":
         fmt.Printf("不及格\n" )
      default:
         fmt.Printf("差\n" )
   }
   fmt.Printf("你的等级是 %s\n", grade )
}    

由上方代码块可知,我们定义了两个局部变量grademarks,对marks进行Switch判断操作,当case满足不同的值的时候,调用函数打印输出的值也不一样


Type Switch


switch 语句还可以被用于 type-switch 来判断某个 interface 变量中实际存储的变量类型


Type Switch语法格式


switch x.(type){
    case type:
       statement(s)      
    case type:
       statement(s)
    /* 你可以定义任意个数的case */
    default: /* 可选 */
       statement(s)
} 

由于Type Switch用途不是特别的多,作者在这里不作详细描述,读者可以去官网自行查询相关文档进行学习


Select定义


  • select 语句类似于 switch 语句,但是select会随机执行一个可运行的case。如果没有case可运行,它将阻塞,直到有case可运行。
  • selectGo中的一个控制结构,类似于用于通信的switch语句。每个case必须是一个通信操作,要么是发送要么是接收
  • select随机执行一个可运行的case。如果没有case可运行,它将阻塞,直到有case可运行。一个默认的子句应该总是可运行的


Select语法格式


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


Select语句注意事项


  • 每个case必须是一个通信
  • 所有channel表达式都会被求值
  • 所有被发送的表达式都会被求值
  • 如果任意某个通信可以进行,它就执行;其他被忽略
  • 如果有多个case都可以运行,Select会随机公平地选出一个执行。其他不会执行。
  • 如果有default子句,则执行该语句。
  • 如果没有default字句,select阻塞,直到某个通信可以运行;Go不会重新对channel值重新进行求值。
package main
import "fmt"
func main() {
   var c1, c2, c3 chan int //通道机制
   var i1, i2 int
   select {
   case i1 = <-c1:
      fmt.Printf("received ", i1, " from c1\n")
   case c2 <- i2:
      fmt.Printf("sent ", i2, " to c2\n")
   case i3, ok := (<-c3): // same as: i3, ok := <-c3
      if ok {
         fmt.Printf("received ", i3, " from c3\n")
      } else {
         fmt.Printf("c3 is closed\n")
      }
   default:
      fmt.Printf("no communication\n")
   }
}

根据上方代码所示,定义了c1、c2、c3三个变量,并且使用chan通道。关于写法的解释:一个可以发送 int 类型数据的 channel 一般写为 chan int,根据上方的语法规则:如果有default子句,则执行该语句,故上方代码块执行代码为:no communication


Select用法补充


  • 我们可以使用select来监听channel的数据流动
  • select的用法与switch语法非常类似,由select开始的一个新的选择块,每个选择条件由case语句来描述
  • switch语句可以选择任何使用相等比较的条件相比select由比较多的限制,其中最大的一条限制就是每个case语句里必须是一个IO操作
  • 如果每个 case都未读取到,则Go语言会自动读取default语句所描述的东西,在正常情况下,每个select程序都会有一个输出语句
  • 如果既没有case语句满足,也不存在default语句,则程序进入阻塞状态系统会发出警告,直至疏通


超时判断


var resChan = make(chan int)
// do request
func test() {
    select {
    case data := <-resChan:
        doData(data)
    case <-time.After(time.Second * 3):
        fmt.Println("request time out")
    }
}
func doData(data int) {
    //...
}

根据上方代码块可知,我们定义了一个select语句,在第一条case里面,我们将resChan传给data如果在传输的过程中时长超过3s,则会执行第二条case语句


退出


var shouldQuit=make(chan struct{})
fun main(){
    {
        //loop
    }
    //...out of the loop
    select {
        case <-c.shouldQuit:
            cleanUp()
            return
        default:
        }
    //...
}
//在另外一个协程中,如果运行遇到非法操作或不可处理的错误,就向shouldQuit发送数据通知程序停止运行
close(shouldQuit)

我们定义一个var类型的shouldQuit变量用于结构体的通道,首先我们开启一个select循环,在case里面调用通道方法,并且返回所对应的值;如果不满足,则返回default的值。同时我们在另外一个协程中,如果我们遇到了非法操作或不可处理的错误,就向shouldQuit发送数据通知程序停止运行


判断Channel状态


ch := make (chan int, 5)
//...
data:=0
select {
case ch <- data:
default:
} 

有时候我们不喜欢缓存变慢,这样不利于我们去释放资源,因此我们可以用一个简单的判断方法去进行判断:首先我们开启一个int类型,长度为5的通道,在通道里面我们开启一个select循环,如果data通道的值能被ch所接收,则执行该条语句,否则我们可对default语句进行抛弃data等处理操作

相关文章
|
22天前
|
存储 Go 开发者
Go语言中的并发编程与通道(Channel)的深度探索
本文旨在深入探讨Go语言中并发编程的核心概念和实践,特别是通道(Channel)的使用。通过分析Goroutines和Channels的基本工作原理,我们将了解如何在Go语言中高效地实现并行任务处理。本文不仅介绍了基础语法和用法,还深入讨论了高级特性如缓冲通道、选择性接收以及超时控制等,旨在为读者提供一个全面的并发编程视角。
|
22天前
|
安全 Go 数据处理
Go语言中的并发编程:掌握goroutine和channel的艺术####
本文深入探讨了Go语言在并发编程领域的核心概念——goroutine与channel。不同于传统的单线程执行模式,Go通过轻量级的goroutine实现了高效的并发处理,而channel作为goroutines之间通信的桥梁,确保了数据传递的安全性与高效性。文章首先简述了goroutine的基本特性及其创建方法,随后详细解析了channel的类型、操作以及它们如何协同工作以构建健壮的并发应用。此外,还介绍了select语句在多路复用中的应用,以及如何利用WaitGroup等待一组goroutine完成。最后,通过一个实际案例展示了如何在Go中设计并实现一个简单的并发程序,旨在帮助读者理解并掌
|
28天前
|
安全 Go 调度
探索Go语言的并发模型:goroutine与channel
在这个快节奏的技术世界中,Go语言以其简洁的并发模型脱颖而出。本文将带你深入了解Go语言的goroutine和channel,这两个核心特性如何协同工作,以实现高效、简洁的并发编程。
|
1月前
|
Go 调度 开发者
探索Go语言中的并发模式:goroutine与channel
在本文中,我们将深入探讨Go语言中的核心并发特性——goroutine和channel。不同于传统的并发模型,Go语言的并发机制以其简洁性和高效性著称。本文将通过实际代码示例,展示如何利用goroutine实现轻量级的并发执行,以及如何通过channel安全地在goroutine之间传递数据。摘要部分将概述这些概念,并提示读者本文将提供哪些具体的技术洞见。
|
2月前
|
安全 Go 调度
探索Go语言的并发之美:goroutine与channel
在这个快节奏的技术时代,Go语言以其简洁的语法和强大的并发能力脱颖而出。本文将带你深入Go语言的并发机制,探索goroutine的轻量级特性和channel的同步通信能力,让你在高并发场景下也能游刃有余。
|
2月前
|
存储 安全 Go
探索Go语言的并发模型:Goroutine与Channel
在Go语言的多核处理器时代,传统并发模型已无法满足高效、低延迟的需求。本文深入探讨Go语言的并发处理机制,包括Goroutine的轻量级线程模型和Channel的通信机制,揭示它们如何共同构建出高效、简洁的并发程序。
|
6月前
|
Go
go之channel关闭与广播
go之channel关闭与广播
|
2月前
|
存储 Go 调度
深入理解Go语言的并发模型:goroutine与channel
在这个快速变化的技术世界中,Go语言以其简洁的并发模型脱颖而出。本文将带你穿越Go语言的并发世界,探索goroutine的轻量级特性和channel的同步机制。摘要部分,我们将用一段对话来揭示Go并发模型的魔力,而不是传统的介绍性文字。
|
2月前
|
安全 Go 调度
探索Go语言的并发模型:Goroutine与Channel的魔力
本文深入探讨了Go语言的并发模型,不仅解释了Goroutine的概念和特性,还详细讲解了Channel的用法和它们在并发编程中的重要性。通过实际代码示例,揭示了Go语言如何通过轻量级线程和通信机制来实现高效的并发处理。
|
2月前
|
安全 Go 调度
探索Go语言的并发之美:goroutine与channel的实践指南
在本文中,我们将深入探讨Go语言的并发机制,特别是goroutine和channel的使用。通过实际的代码示例,我们将展示如何利用这些工具来构建高效、可扩展的并发程序。我们将讨论goroutine的轻量级特性,channel的同步通信能力,以及它们如何共同简化并发编程的复杂性。