select

Linux很早就引入的函数,用来实现异步非阻塞的一种方式。

Go语言直接在语言级别支持select关键字,用于处理异步IO问题。

select{
    case <- chan1:  //如果chan1成功读到数据,则进行该case处理语句
    case chan2 <- 1:   //如果成功向chan2写入数据,则进行该case处理语句
    default:     //如果上面都没有成功,则进入default处理流程
}

首先体验一下select的用法:

//select.go
package main

import (
	"fmt"
)

func main(){
	ch := make(chan int)

	select {
	case <- ch:    //没人写入,阻塞在这里
		fmt.Print("come to read ch!")
	default:      //如果前面的case都没有准备好,默认走到default
		fmt.Print("come to default!")
	}
}

输出结果:

come to default!

创建一个匿名函数协程

//select.go
package main

import (
	"fmt"
	"time"
)

func main(){
	ch := make(chan int)

	go func(ch chan int){        //匿名函数
		ch <- 1
	}(ch)

	time.Sleep(time.Second)    //让主协程睡一下  让他主动去执行上面匿名函数的协程
	         //不然到select的时候ch还是不能读
	select {
	case <- ch:    //没人写入,阻塞在这里
		fmt.Print("come to read ch!")
	default:      //如果前面的case都没有准备好,默认走到default
		fmt.Print("come to default!")
	}
}

输出结果:

come to read ch!

一个超时控制的经典实现

timeout := make(chan bool,1)
go func(){
    time.Sleep(1e9)   //等待1秒钟
    timeout <- true
}()

//然后我们把timeout这个channel利用起来
select{
    case <- ch:    //从ch中读取到数据
    case <- timeout:     //一直没有从ch中读取到数据,但从timeout中读取到了数据
}

实现例子如下:

package main

import (
	"fmt"
	"time"
)

func main(){
	ch := make(chan int)
	timeout := make(chan int , 1)

	go func(){
		time.Sleep(time.Second)
		timeout <- 1
	}()

	go func(ch chan int){
		ch <- 1
	}(ch)

	//time.Sleep(time.Second)    //让主协程睡一下  让他主动去执行上面匿名函数的协程
	         //不然到select的时候ch还是不能读
	select {
	case <- ch:    //没人写入,阻塞在这里
		fmt.Print("come to read ch!")
	case <- timeout:      //如果前面的case都没有准备好,默认走到default
		fmt.Print("come to timeout!")
	}
}

一秒钟的超时控制,如果没有ch写入,也会进入case <- timeout。

time.After方法进行超时控制

没有运用协程。

package main

import (
	"fmt"
	"time"
)

func main() {
	ch := make(chan int)

	select {
	case <-ch:
		fmt.Print("come to read ch!")
	case <-time.After(time.Second):
		fmt.Print("come to timeout!")
	}

	fmt.Print("end of code!")
}