Go基础编程 - 12 -流程控制

流程控制

  • 1. 条件语句
    • 1.1. if...else 语句
    • 1.2. switch 语句
    • 1.3. select 语句
      • 1.3.1. select 语句的通信表达式
      • 1.3.2. select 的基特性
      • 1.3.3. select 的实现原理
      • 1.3.4. 经典用法
        • 1.3.4.1 超时控制
        • 1.3.4.2 多任务并发控制
        • 1.3.4.3 监听多通道消息
        • 1.3.4.4 default 实现非堵塞读写
  • 2. 循环语句
    • 2.1. for 语句
    • 2.2. for ... range
  • 3. goto、break、continue

上一篇:函数
下一篇:单元测试


1. 条件语句

Go语言中,条件语句分为三种:if语句、switch语句和select语句。

1.1. if…else 语句

if语句基本格式如下:

if 条件 {// 条件满足时执行的语句
} else if 条件 {// 条件满足时执行的语句
} else {// 条件为false时执行的语句
}

不支持三元操作符(三目运算符) :“a > b ? a : b”

1.2. switch 语句

基于不同的条件执行不同的动作,每个case分支都是唯一的,从上至下逐一匹配,直到匹配到一个case分支,执行该分支的代码,并终止匹配。

switch语句基本格式如下:

switch{
case1:// 值等于值1时执行的语句
case2:// 值等于值2时执行的语句
default:// 值不等于值1和值2时执行的语句
}
  • 如果switch没有表达式,它会匹配true。
  • case 分支表达式可以是任意类型,不限于常量,但必须是相同类型
  • 一个 case 可以同时测试多个值,用逗号分隔。例如:case val1, val2, val3:
  • 一个 case 分支可以使用fallthrough语句,匹配成功后强制执行相邻的下一个case语句,且fallthrough不可使用在最后一个分支。
  • Go语言的switch默认相当于每个case最后带有break,可省略。

示例:

package mainimport "fmt"func main() {var i = 0switch i {case 0:println("0")    println("fallthrough")  // 使用fallthrough语句,匹配成功后强制执行下一个case代码:case 1,2fallthroughcase 1, 2:  // 一个 case 可以同时测试多个值,用逗号分隔。fmt.Println("1 或 2")case 3:fmt.Println("3")default:fmt.Println("default")// fallthrough  // fallthrough不可使用在最后一个分支}// 输出结果:// 0// fallthrough// 1 或 2var n = 6switch { //省略条件表达式默认为true,可当 if...else if...elsecase n > 0 && n < 10:fmt.Println("i > 0 and i < 10")case n > 10 && n < 20:fmt.Println("i > 10 and i < 20")default:fmt.Println("def")}// 输出结果:// i > 0 and i < 10
}

1.3. select 语句

select是Go中的一个控制结构,类似 switch 语句,用于处理异步IO操作。但是select用于等待多个通信操作的完成,会随机执行一个可运行的case;如果没有case可运行,它将阻塞,直到有case可运行。

1.3.1. select 语句的通信表达式

  • 一个通信操作,如:ch <- vv := <-ch
  • 一个接收表达式,如:v := <-ch
  • 一个发送表达式,如:ch <- v
  • 一个默认通信,如:default

1.3.2. select 的基特性

  • case 语句必须是一个 cannel 操作,要么是发送,要么是接收。
  • select 中 default 语句总是可执行的(一般不写在里面,因为会很消CPU资源)。
  • 如果任意某个通信可以执行,它就执行;其它被忽略。
  • 如果有多个 case 都可以运行,select 会随机公平地选出一个执行;其它不会执行。
  • 如果没有可运行的 case 语句,且有 default 语句,那么就会执行 default 的动作。
  • 如果没有可运行的 case 语句,且没有 default 语句,select 将阻塞,直到某个 case 通信可以运行。

1.3.3. select 的实现原理

参考:Go select 底层原理、select 的随机公平策略理

在这里插入图片描述

无 case 永久堵塞

select{} // fatal error: all goroutines are asleep - deadlock!
// 
// goroutine 1 [select (no cases)]:

select 所有 case 均无法执行且没有 default,则阻塞

ch := make(chan struct{})
select {
case data <- ch:  // 只有一个 case,实际会被编译器转换为相应 channel 相应的收发操作,其实和实际调用 data := <- ch 并没有什么区别fmt.Printf("ch data: %v\n", data)
}// fatal error: all goroutines are asleep - deadlock!
// 
// goroutine 1 [chan receive]:

select多个case同时可以执行,随机选择一个去执行

package mainimport "fmt"func main() {var c1 = make(chan int, 2)c2 := make(chan string, 2)c3 := make(chan int, 2)var i1 inti2 := "two"c1 <- 1c2 <- "one"c3 <- 3select {case i1 = <-c1:fmt.Printf("received %d from c1\n", i1)case c2 <- i2:fmt.Printf("send %s to c2\n", i2)case i3, ok := <-c3: // same as: i3, ok := (<-c3)if ok {fmt.Printf("received %d from c3\n", i3)} else {fmt.Printf("c3 is closed\n")}default:fmt.Printf("no communication\n")}
}
//随机输出下面一条:
// send two to c2
// received 1 from c1
// received 3 from c3

1.3.4. 经典用法

1.3.4.1 超时控制
package mainimport ("fmt""time"
)func main() {ch := make(chan int)go func() {time.Sleep(3 * time.Second)ch <- 1}()select {case data, ok := <-ch:if ok {fmt.Println("接收到数据: ", data)} else {fmt.Println("通道已被关闭")}case <-time.After(2 * time.Second):fmt.Println("超时了!")}
}// 超时了!
1.3.4.2 多任务并发控制
package mainimport ("fmt"
)func main() {ch := make(chan int)for i := 0; i < 10; i++ {go func(id int) {ch <- id}(i)}for i := 0; i < 10; i++ {select {case data, ok := <-ch:if ok {fmt.Println("任务完成:", data)} else {fmt.Println("通道已被关闭")}}}
}// 每次执行,顺序不一致
// 任务完成: 2
// 任务完成: 0
// 任务完成: 1
// 任务完成: 4
// 任务完成: 3
// 任务完成: 6
// 任务完成: 5
// 任务完成: 7
// 任务完成: 8
// 任务完成: 9
1.3.4.3 监听多通道消息
package mainimport ("fmt""time"
)func main() {ch1 := make(chan int)ch2 := make(chan int)// 开启 goroutine 1 用于向通道 ch1 发送数据go func() {for i := 0; i < 5; i++ {ch1 <- itime.Sleep(time.Second)}}()// 开启 goroutine 2 用于向通道 ch2 发送数据go func() {for i := 5; i < 10; i++ {ch2 <- itime.Sleep(time.Second)}}()// 主 goroutine 从 ch1 和 ch2 中接收数据并打印for i := 0; i < 10; i++ {select {case data := <-ch1:fmt.Println("Received from ch1:", data)case data := <-ch2:fmt.Println("Received from ch2:", data)}}fmt.Println("Done.")
}
1.3.4.4 default 实现非堵塞读写
import ("fmt""time"
)func main() {ch := make(chan int, 1)go func() {for i := 1; i <= 5; i++ {ch <- itime.Sleep(1 * time.Second)}close(ch)}()for {select {case val, ok := <-ch:if ok {fmt.Println(val)} else {ch = nil}default:fmt.Println("No value ready")time.Sleep(500 * time.Millisecond)}if ch == nil {break}}
}

2. 循环语句

2.1. for 语句

Go语言 for 循环有 3 种形式。

for init; condition; post{}
for condition {}
for {}

示例:

package mainimport "fmt"func main() {for i := 0; i < 10; i++ {fmt.Println("i =", i)}fmt.Println()n := 10for n > 0 {fmt.Println("n =", n)n--}fmt.Println()j := 0for {if j >= 10 {break}fmt.Println("j =", j)j++}fmt.Println()
}

2.2. for … range

range 类似迭代器操作,返回(索引, 值)或(键, 值)。

for ... range 格式可以对 slice、map、数组、字符串等进行迭代循环。格式如下:

for key, value := range oldMap {}

示例:

package mainimport "fmt"func main() {s := "string中文"for i, v := range s {fmt.Printf("%d, %d, %T\n", i, v, v)}println()// 忽略值for i := range s {fmt.Printf("%d\n", i)}// 忽略返回值,仅迭代for range s {}println()println()a := [3]int{0, 1, 2}for i, v := range a {if i == 0 {a[1], a[2] = 222, 333 // range 会复制对象,修改 a 不影响 i, v 的值}fmt.Println(i, v, a) // 输出:[0 222 333]a[i] = 100 + v}fmt.Println(a) // 输出:[100 101 102]println()println()si := []int{1, 2, 3, 4, 5}for i, v := range si {if i == 0 {si = si[:3] // 对 slice 的修改,不会影响 rangesi[2] = 100 // 对底层数据的修改,影响 range}fmt.Printf("%d, %d, %v\n", i, v, si)}
}

for...range 可以遍历 channel, 与遍历 map、slice 不同

package mainimport "fmt"func main() {queue := make(chan string, 2)for i := 0; i < 10; i++ {queue <- "-data-" + strconv.Itoa(i)}close(queue)// 这个 `range` 迭代从 `queue` 中得到的每个值。// 因为我们在前面 `close` 了这个通道,这个迭代会在接收完 queue 中的值之后结束;for elem := range queue {fmt.Println(elem)}// 如果我们没有 `close` 它,我们将在这个循环中继续阻塞执行,等待接收下一个个值。类似下面代码 for {}for {fmt.Println(<- queue)}
}

3. goto、break、continue

break:跳出循环。
continue:跳过当前循环,继续下一次循环。仅限 for 循环内使用。
goto:通过label跳转到指定位置。

  • 三个标签都可以配合标签(label)使用。
  • continue, break 配合标签可用于多层循环跳出。
  • goto 是调整执行位置,跳到指定标签代码块执行;continue 配合标签为跳到指定循环继续执行, break 配合标签跳出指定标签代码块的循环。
package mainimport "Learing/demo"func main() {// 标签区名分大小写,若定义标签不使用则会照成编译错误。
LabelBreak: // break 的跳转标签放在循环语句前面for {for i := 0; i < 10; i++ {if i > 2 {break LabelBreak // break, 跳出 LabelBreak 标签代码块,不再执行循环}fmt.Println("break -", i)}}fmt.Println()LableContinue:  // continue 的跳转标签放在循环语句前面for i := 0; i < 5; i++ {for {fmt.Println("before", i)continue LableContinue // continue, 跳到LableContinue标签的循环,继续执行循环。fmt.Println("after")}}fmt.Println("continue label")fmt.Println()for {for i := 0; i < 10; i++ {if i > 2 {goto LabelGoto // goto, 跳转到指定标签代码块}fmt.Println("goto -", i)}}
LabelGoto:fmt.Println("goto label")fmt.Println()
}func SelectBreak() {ch := make(chan int, 2)go func() {for i := 0; i < 10; i++ {ch <- i}}()Label:for {select {case v, ok := <-ch:fmt.Println(ok, v)if v == 5 {break Label // 在 for 的 select 体中 break 到外层循环}default:fmt.Println("The ch value is not ready.")time.Sleep(500 * time.Millisecond)}}fmt.Println("Break for select")
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/874892.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

FPGA读写操作SRAM_CY7C1051DV33

手上有一块sram需要验证下功能是否正常&#xff0c;我门通过fpga来进行读写测试。 1.首先看下芯片手册&#xff0c;我们重点关注时序部分 总结下&#xff0c;就是读写时间不能小于10nS,也就是最高频率100M&#xff0c;所以我们程序设计按100M时钟速率进行设计。注意&#x…

构建稳固与安全的网络环境:从微软蓝屏事件看软件更新流程与应急响应

“微软蓝屏”事件暴露了网络安全哪些问题&#xff1f; 近日&#xff0c;由微软视窗系统软件更新引发的全球性“微软蓝屏”事件&#xff0c;不仅让科技领域为之震动&#xff0c;更是一次对全球IT基础设施韧性与安全性的深刻检验。这次事件源于美国电脑安全技术公司“众击”的一…

2024-07-23 Unity插件 Odin Inspector11 —— 使用 Odin 自定义编辑窗口

文章目录 1 OdinEditorWindow1.1 运作方式1.2 使用特性绘制 OdinEditorWindow1.3 在 OdinEditorWindow 中渲染对象 2 OdinMenuEditorWindow2.1 添加菜单导航栏2.2 添加导航栏示例 ​ Odin Window 可以完整地访问 Odin 绘图系统&#xff0c;不再需要操心 Window 的绘制 方式&am…

BGP选路之Local Preference

原理概述 当一台BGP路由器中存在多条去往同一目标网络的BGP路由时&#xff0c;BGP协议会对这些BGP路由的属性进行比较&#xff0c;以确定去往该目标网络的最优BGP路由。BGP首先比较的是路由信息的首选值&#xff08;PrefVal)&#xff0c;如果 PrefVal相同&#xff0c;就会比较本…

全方位了解智慧校园行政办公的新闻管理功能

在智慧校园的日常运营中&#xff0c;行政办公系统中的新闻公告功能犹如一座沟通的桥梁&#xff0c;连接着校园内外的每一个角落&#xff0c;传递着最新的资讯与动态。它不仅是智慧校园信息发布的平台&#xff0c;更是校园文化与精神风貌的展现窗口&#xff0c;对于增强师生的凝…

JavaWeb(4)JavaScript入门2—— JS的对象和JSON

一、JS的对象 1.声明语法1 通过new Object()直接创建对象 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><ti…

C#与C++交互开发系列(三):深入探讨P/Invoke基础知识

欢迎来到C#与C交互开发系列的第三篇。在这篇博客中&#xff0c;我们将深入探讨P/Invoke&#xff08;Platform Invocation Services&#xff09;的基础知识。P/Invoke是C#调用非托管代码的一种机制&#xff0c;能够让C#直接调用C编写的动态链接库&#xff08;DLL&#xff09;中的…

外六角半螺纹螺丝主要应用领域

外六角半螺纹螺丝&#xff0c;作为一种常见的紧固件&#xff0c;因其独特的设计和多样的功能而在多个工业领域中占据着重要的地位。这种螺丝的一端具有完整的螺纹&#xff0c;而另一端则可能没有螺纹或螺纹较短&#xff0c;这样的设计使其在某些应用场景中具有独特的优势。 应用…

docker文件挂载和宿主主机文件的关系

一、背景 在排查docker日志时发现在读取docker的文件时找不到文件&#xff0c;在宿主主机上可以查到对应的文件。这里就要理解docker文件目录和宿主主机上的文件的关系。 二、Docker文件系统和宿主系统 Docker文件和宿主文件之间的关系主要体现在Docker容器的运行环境中。Docke…

【目录】8051汇编与C语言系列教程

8051汇编与C语言系列教程 作者将狼才鲸创建日期2024-07-23 CSDN文章地址&#xff1a;【目录】8051汇编与C语言系列教程本Gitee仓库原始地址&#xff1a;才鲸嵌入式/8051_c51_单片机从汇编到C_从Boot到应用实践教程 一、本教程目录 序号教程名称简述教程链接1点亮LCD灯通过IO…

【分布式锁】Redission实现分布式锁

接着上一节&#xff0c;我们遇到了超卖的问题&#xff0c;并通过Redis实现分布式锁&#xff0c;进行了解决。本节 我将换一种方式实现分布式锁。 前提&#xff1a; nginx、redis、nacos 模块1&#xff1a; provider-and-consumer 端口 8023 模块2 rabbitmq-consumer 端口 8021 …

成都云飞浩容文化传媒有限公司电商服务新领航者

在电商的海洋里&#xff0c;无数企业如同繁星般闪烁&#xff0c;但其中有一颗璀璨的明星——成都云飞浩容文化传媒有限公司&#xff0c;以其专业的电商服务和独特的创新理念&#xff0c;成为行业的佼佼者。今天&#xff0c;就让我们一起走进云飞浩容&#xff0c;探索这家公司的…

# Redis 入门到精通(八)-- 服务器配置-redis.conf配置与高级数据类型

Redis 入门到精通&#xff08;八&#xff09;-- 服务器配置-redis.conf配置与高级数据类型 一、redis 服务器配置–redis.conf 配置 1、服务器端设定 1&#xff09;设置服务器以守护进程的方式运行&#xff1a; daemonize yes|no 2&#xff09;绑定主机地址&#xff1a; bin…

【防火墙】防火墙NAT、智能选路综合实验

实验拓扑 实验要求 7&#xff0c;办公区设备可以通过电信链路和移动链路上网(多对多的NAT&#xff0c;并且需要保留一个公网IP不能用来转换) 8&#xff0c;分公司设备可以通过总公司的移动链路和电信链路访问到Dmz区的http服务器 9&#xff0c;多出口环境基于带宽比例进行选路…

自动驾驶AVM环视算法–更新超广角视图算法和exe测试demo

1、测试环境 opencv310vs2022 2、使用的编程语言 c和c 3、测试的demo的获取 更新&#xff1a;测试的exe程序&#xff0c;无需解压码就可以体验算法测试效果 百度网盘&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1ndDcJ6VwexZoLUyUXlwLwg 提取码&#xff1a;q1…

python-爬虫实例(1):获取京东商品评论

目录 前言 道路千万条&#xff0c;安全第一条 爬虫不谨慎&#xff0c;亲人两行泪 获取京东商品评论信息 一、实例示范 二、爬虫四步走 1.UA伪装 2.获取Url 3.发送请求 4.获取响应数据进行解析并保存 总结 前言 道路千万条&#xff0c;安全第一条 爬虫不谨慎&#xff0c;…

手机怎么恢复回收站清空的照片?2个技巧大公开(适用各类安卓机)

站在时间的边缘&#xff0c;凝视着那些即将消逝的回忆&#xff0c;心里的惆怅不断增加。我们到底该怎么恢复回收站删除的照片呢&#xff1f;请相信&#xff0c;这些回忆并未真正消失。 无论你是因为误操作还是其他原因导致回收站被清空&#xff0c;这篇文章都将为你提供有力的…

Centos7_Minimal安装Cannot find a valid baseurl for repo: base/7/x86_6

问题 运行yum报此问题 就是没网 解决方法 修改网络信息配置文件&#xff0c;打开配置文件&#xff0c;输入命令&#xff1a; vi /etc/sysconfig/network-scripts/ifcfg-网卡名字把ONBOOTno&#xff0c;改为ONBOOTyes 重启网卡 /etc/init.d/network restart 网路通了

慎用Float和Double进行浮点运算

背景 在之前的开发过程中, 遇到了一些小问题. 就是在某功能计算时, 按照当时的设想是需要保留两位小数并向下取整. 当时没有太好的思路, 于是请教了好朋友gpt同志. 而gpt给出3种思路: 使用String.format方法 double value 123.456789; String formattedString String.forma…

Redis从入门到超神-(五)Redis实现分布式锁原理

引言 什么是分布式锁&#xff1f; 分布式锁是分布式系统中用于控制多个进程或线程对共享资源的访问的一种机制。在分布式系统中&#xff0c;由于存在多个服务实例或节点&#xff0c;它们可能会同时尝试访问或修改同一份数据或资源。如果没有适当的同步机制&#xff0c;就可能导…