go语言的select调度器的用法

4 篇文章 0 订阅
4 篇文章 0 订阅

go语言的select调度器的用法

原理

select调度是使用了case来实现非阻塞式的通信,普通的通信是向channel中发送了数据,就必须有人接收,如果还没接收就再发送,就会阻塞住。
而case语句的特点就是如果不满足后面语句这个条件,就会继续判断下一个case语句,所以将收发数据的语句放在case中,这样就避免了阻塞。

代码实例

//先定义一个生成器,生成一个不断发送数据的channel
func produce() chan int{
    out := make(chan int)
    go func(){
        for{
            time.Sleep(5 * time.Second)
            out <- 0
        }
    }()
    return out
}

//然后在main函数中创建两个channel接收数据
func main(){
    c1,c2 := produce(),produce()
    //这样就会接收一次,如果都还没有发送数据就会走default,否则就会从c1或c2中接收一个数据,然后就会退出
    select{
        case n := <- c1:
            fmt.Println(n)
        case n := <- c2:
            fmt.Println(n)
        default:
            fmt.Println(not recived data)
    }
}

//也可以写一个循环持续接收
func main(){
    c1,c2 := produce(),produce()
    for{
        select{
            case n := <- c1:
                fmt.Println(n)
            case n := <- c2:
                fmt.Println(n)
            default:
                fmt.Println(not recived data)
        }
    }
}

扩展

利用select调度的特点,简单地实现了非阻塞式的通信。在此基础上,我们还可以对其进行扩展,增加一些功能,同时保持非阻塞的性质。
例如

  • 扩展1:接收到数据后不是直接输出,而是继续发送给另一个channel
  • 扩展2:创建一个定时器,一定时间过后退出程序
  • 扩展3:创建个定时器,每隔一段时间输出内容

扩展1

定义生成器,创建一个接收数据的channel

func createRecChan() chan int{
    out := make(chan int)
    go func(){
        //这种写法会依次取完channel中的数据,如果channel中没有数据就停止接收
        for i := range out{
            fmt.Println(i)
        }
    }()
    return out
}

main函数中需要修改一下

//1. 简单粗暴的修改
func main(){
    c1,c2 := produce(),produce()
    w := createRecChan()
    for{
        select{
            case n := <- c1:
                w <- n
            case n := <- c2:
                w <- n
            default:
                fmt.Println(not recived data)
        }
    }
}
//这样存在的问题
//不能够继续非阻塞式的通信了
//假设第一次循环执行的是第一个case语句,会向w中发送一次数据,如果w的数据还没有被接收就到了第二次循环,还是执行的第一个case语句,这时又往w发送数据,这样就会阻塞住

//2. 再稍微修改一下
func main(){
    c1,c2 := produce(),produce()
    w := createRecChan()
    //将向w发送数据的动作也写成一个case语句,这样就避免了阻塞
    //将n提出来定义,这样向w发送数据才能用到n
    n := 0
    for{
        select{
            case n = <- c1:
            case n = <- c2:
            case w <- n:
            default:
                fmt.Println(not recived data)
        }
    }
}
//这样存在的问题
//如果没有从c1,c2中接收到数据,n还是有值的,就会一直将当前n的值发送给w
//所以需要加一个判断,当从c1或c2中接收到数据了,才向w发送数据

//3. 再再稍微修改一下
func main(){
    c1,c2 := produce(),produce()
    w := createRecChan()
    n := 0
    //定义一个标记,根据这个标记来判断是否接收了数据,初始赋为false
    isHaveData := false   
    for{
        //每一次循环定义一遍,然后再根据标记判断是否将w赋给实际要发送的channel
        //外面定义的话赋一次值,后面就一直有值了,不合理
        var actualChan chan int
        if isHaveData{
            actualChan = w
        }
        select{
            case n = <- c1:
                isHaveData = true
            case n = <- c2:
                isHaveData = true
            case actualChan <- n:
                isHaveData = false
            default:
                fmt.Println(not recived data)
        }
    }
}
//这样还是存在问题
//因为每次从c1或c2接收到数据后就会更改n的值,如果没有及时将n发送出去,就又接收到了数据,
//这样n的值就被改变了,导致会丢失很多数据
//可以使用一个slice来存储接收到的数据

//4. 再再再稍微修改一下
func main(){
    c1,c2 := produce(),produce()
    w := createRecChan()
    n := 0  
    //定义一个slice存放接收到的数据
    var value []int
    for{
        //每一次循环定义一遍,然后再根据标记判断是否将w赋给实际要发送的channel
        //外面定义的话赋一次值,后面就一直有值了,不合理
        var actualChan chan int
        //不适用标记了,而是直接判断slice的长度来判断是否接收到了数据
        if len(value){
            actualChan = w
            //每次发送slice中第一个数据
            actualValue = value[0]
        }
        select{
            //每次接收到数据存到slice中
            case n = <- c1:
                value = append(value,n)
            case n = <- c2:
                value = append(value,n)
            //发送数据后,从slice中取出
            case actualChan <- actualValue:
                value = value[1:]
            default:
                fmt.Println(not recived data)
        }
    }
}

扩展2

定时机制,超过一定时间退出程序
使用time.After方法

用法

time.After(time.Duration)返回的是一个channel,当经过一定时间后向该channel发送数据(只发送一次)。

通过这个特性,我们可以先创建一个channel,然后select调度器中从这个channel中接收数据,接收到了说明经过了设定好的时间,就可以退出程序了

代码

//在之前代码基础上添加
func main(){
    c1,c2 := produce(),produce()
    w := createRecChan()
    n := 0  
    //定义一个slice存放接收到的数据
    var value []int

    //创建一个定时器channel,10秒后发送数据
    end := time.After(10 * time.Second)
    for{
        //每一次循环定义一遍,然后再根据标记判断是否将w赋给实际要发送的channel
        //外面定义的话赋一次值,后面就一直有值了,不合理
        var actualChan chan int
        //不适用标记了,而是直接判断slice的长度来判断是否接收到了数据
        if len(value){
            actualChan = w
            //每次发送slice中第一个数据
            actualValue = value[0]
        }
        select{
            //每次接收到数据存到slice中
            case n = <- c1:
                value = append(value,n)
            case n = <- c2:
                value = append(value,n)
            //发送数据后,从slice中取出
            case actualChan <- actualValue:
                value = value[1:]
            //接收到数据,说明时间到了,退出程序
            case <- end
                fmt.Println("good bye")
                return
            default:
                fmt.Println(not recived data)
        }
    }
}

扩展3

定时机制,间隔一定时间发送数据
使用time.Tick方法

用法

time.Tick(time.Duration)返回的是一个channel,每间隔一定时间后向该channel发送数据(不只发送一次)。

通过这个特性,我们可以先创建一个channel,然后select调度器中从这个channel中接收数据,接收到了说明经过了设定好的时间

代码

//在之前代码基础上添加
func main(){
    c1,c2 := produce(),produce()
    w := createRecChan()
    n := 0  
    //定义一个slice存放接收到的数据
    var value []int

    //创建一个定时器channel,10秒后发送数据
    end := time.After(10 * time.Second)
    //创建一个定时器channel,每秒后发送一次数据
    tick := time.Tick(time.Second)
    for{
        //每一次循环定义一遍,然后再根据标记判断是否将w赋给实际要发送的channel
        //外面定义的话赋一次值,后面就一直有值了,不合理
        var actualChan chan int
        //不适用标记了,而是直接判断slice的长度来判断是否接收到了数据
        if len(value){
            actualChan = w
            //每次发送slice中第一个数据
            actualValue = value[0]
        }
        select{
            //每次接收到数据存到slice中
            case n = <- c1:
                value = append(value,n)
            case n = <- c2:
                value = append(value,n)
            //发送数据后,从slice中取出
            case actualChan <- actualValue:
                value = value[1:]
            //接收到数据,说明时间到了,退出程序
            case <- end
                fmt.Println("good bye")
                return
            //接收到数据,说明时间又过去了1秒钟
            case <- tick
                fmt.Println("又过了1秒")
            default:
                fmt.Println(not recived data)
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值