11_流程控制

一、条件语句 select

1.1 select 介绍

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

select { //不停的在这里检测
    case <-chanl :    //检测有没有数据可以读
    //如果chanl成功读取到数据,则进行该case处理语句
    case chan2 <- 1 : //检测有没有可以写
    //如果成功向chan2写入数据,则进行该case处理语句
    //假如没有default,那么在以上两个条件都不成立的情况下,就会在此阻塞//一般default会不写在里面,select中的default子句总是可运行的,因为会很消耗CPU资源
    default:
    //如果以上都没有符合条件,那么则进行default处理流程
} 

​ select 语句类似于 switch 语句,但是 select会随机选择一个可运行的 case,如果没有 case可以运行,那么它将一直阻塞直到有 case可用。select 语句中的每个 case 都必须是一个通信操作,要么是发送要么是接收,如果有多个 case 都可以运行,Select 会随机公平地选择出一个执行,其它的不会执行;如果有 default 子句,那么执行该语句;如果没有 default 子句,那么 select 将阻塞,直到某个通信可以运行。

//实例
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):
		if ok {
			fmt.Printf("received ", i3, "from c3\n")
		} else {
			fmt.Printf("c3 is closed\n")
		}
		default:
			fmt.Println("no communication\n")
	}
}
//output
no communication

1.2 select 典型用法

1.2.1 超时判断

//比如在下面的场景中,使用全局resChan来接受response,如果时间超过3S,resChan中还没有数据返回,则第二条case将执行
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) {
    //...
}

1.2.2 线程/协程 退出

//主线程(协程)中如下:
var shouldQuit=make(chan struct{})
fun main(){
    {
        //loop
    }
    //...out of the loop
    select {
        case <-c.shouldQuit:
            cleanUp()
            return
        default:
        }
    //...
}

//再另外一个协程中,如果运行遇到非法操作或不可处理的错误,就向shouldQuit发送数据通知程序停止运行
close(shouldQuit)

1.2.3 判断channel是否阻塞

//在某些情况下是存在不希望channel缓存满了的需求的,可以用如下方法判断
ch := make (chan int, 5)
//...
data:=0
select {
	case ch <- data:
	default:
    	//做相应操作,比如丢弃data。视需求而定
} 

二、循环语句 range

//例1
func main() {
    a := [3]int{0, 1, 2}

    for i, v := range a { // index、value 都是从复制品中取出。

        if i == 0 { // 在修改前,我们先修改原数组。
            a[1], a[2] = 999, 999
            fmt.Println(a) // 确认修改有效,输出 [0, 999, 999]。
        }

        a[i] = v + 100 // 使用复制品中取出的 value 修改原数组。

    }

    fmt.Println(a) // 输出 [100, 101, 102]。
}  

//output
[0 999 999]
[100 101 102]   
func main() {
    s := []int{1, 2, 3, 4, 5}

    for i, v := range s { // 复制 struct slice { pointer, len, cap }。

        if i == 0 {
            s = s[:3]  // 对 slice 的修改,不会影响 range,这里不是对底层数据的修改,所以无影响
            s[2] = 100 // 对底层数据的修改。
        }

        println(i, v)
    }
}  
//output
0 1
1 2
2 100
3 4
4 5

​ 对数组使用 range-for 会复制一份底层数组,index、value均从复制品中取出;slice 作为引用类型被 range-for 时不会复制底层数据,对于底层数据的修改会影响到 range 循环。

type student struct {
    name string
    age  int
}

func main() {
    m := make(map[string]*student)
    stus := []student{
        {name: "pprof.cn", age: 18},
        {name: "测试", age: 23},
        {name: "博客", age: 28},
    }

    for _, stu := range stus {
        m[stu.name] = &stu     //这里每次取复制对象的地址,都是同一个地址,取得最后循环到的对象的值
    }
    for k, v := range m {
        fmt.Println(k, "=>", v.name)
    }
} 

//output
pprof.cn => 博客
测试 => 博客
博客 => 博客
posted @ 2023-10-08 13:11  Stitches  阅读(22)  评论(0)    收藏  举报