zoukankan      html  css  js  c++  java
  • Go Select使用

     Go Select使用

    Go中的select和channel配合使用,通过select可以监听多个channel的I/O读写事件,当 IO操作发生时,触发相应的动作。

    基本用法

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

    使用规则

    1.如果没有default分支,select会阻塞在多个channel上,对多个channel的读/写事件进行监控。
    2.如果有一个或多个IO操作可以完成,则Go运行时系统会随机的选择一个执行,否则的话,如果有default分支,则执行default分支语句,如果连default都没有,则select语句会一直阻塞,直到至少有一个IO操作可以进行。   

    快速返回

    同时监听不同的channel,做同一件工作,可以最快的返回结果。

    package main
    
    import (
    	"fmt"
    	"github.com/kirinlabs/HttpRequest"
    )
    
    func main() {
    	ch1 := make(chan int)
    	ch2 := make(chan int)
    	ch3 := make(chan int)
    	go Getdata("https://www.baidu.com",ch1)
    	go Getdata("https://www.baidu.com",ch2)
    	go Getdata("https://www.baidu.com",ch3)
    	select{
    		case v:=<- ch1:
    			fmt.Println(v)
    		case v:=<- ch2:
    			fmt.Println(v)
    		case v:=<- ch3:
    			fmt.Println(v)
    	}
    }
    
    func Getdata(url string,ch chan int){
    	req,err := HttpRequest.Get(url)
    	if err != nil{
    
    	}else{
    		ch <- req.StatusCode()
    	}
    }

    随机返回

    同时监控不同的channel,配上default,select也不会阻塞。

    package main
    
    import (
    	"fmt"
    	"github.com/kirinlabs/HttpRequest"
    )
    
    func main() {
    	ch1 := make(chan int)
    	ch2 := make(chan int)
    	ch3 := make(chan int)
    	go func(){
    		for {
    			Getdata("https://www.baidu.com", ch1)
    			Getdata("https://cn.bing.com", ch2)
    			Getdata("https://cn.bing.com", ch3)
    		}
    	}()
    	go func(){
    		for {
    			select {
    				case v := <-ch1:
    					fmt.Println("信道1的结果:",v)
    				case v := <-ch2:
    					fmt.Println("信道2的结果:",v)
    				case v := <-ch3:
    					fmt.Println("信道3的结果:",v)
    				default:
    					continue
    			}
    		}
    	}()
    	select{}
    }
    
    func Getdata(url string,ch chan int){
    	req,err := HttpRequest.Get(url)
    	if err != nil{
    
    	}else{
    		ch <- req.StatusCode()
    	}
    }

    通过select来检测channel的关闭事件

    func TestSelect1() {
        start := time.Now()
        c := make(chan interface{})
    
        go func() {
            time.Sleep(2*time.Second)
            close(c)
        }()
    
        fmt.Println("Blocking on read...")
        select {
        case <-c:
            fmt.Printf("Unblocked %v later.
    ", time.Since(start))
        }
    }

    注意:当close channel时,读取channel的一方会从channel中读取到value,false,此时的value一般情况下为nil。
    该例子也可以用来通知当不使用channel时,关闭channel的情况。 

    通过channel通知,从而退出死循环

    func TestExitLoop() {
        done := make(chan interface{})
    
        go func() {
            time.Sleep(2*time.Second)
            close(done)
        }()
    
        workCounter := 0
    loop:
        for {
            select {
            case <-done:
                break loop
            default:
            }
    
            // Simulate work
            workCounter++
            time.Sleep(1*time.Second)
        }
    
        fmt.Printf("在通知退出循环时,执行了%d次.
    ", workCounter)
    }
    

    启动一个goroutine,该goroutine在2s后,关闭channel。此时,主协程会在select中的case <-done分支中得到通知,跳出死循环。而在此之前,会执行default分支的代码,这里是什么都不做。

    超时机制

    package main
    import (
    	"fmt"
    	"time"
    )
    func main() {
    	ch := make(chan int)
    	quit := make(chan bool)
    	//新开一个协程
    	go func() {
    		for {
    			select {
    			case num := <-ch:
    				fmt.Println("num = ", num)
    			case <-time.After(3 * time.Second):
    				fmt.Println("超时")
    				quit <- true
    			}
    		}
    	}() 
    	for i := 0; i < 5; i++ {
    		ch <- i
    		time.Sleep(time.Second)
    	}
    	<-quit
    	fmt.Println("程序结束")
    }

    死锁与默认情况

    package main
    
    func main() {  
        ch := make(chan string)    
        select {    
            case <-ch:
        }
    }

    上面的程序中,我们在第 4 行创建了一个信道 ch。我们在 select 内部(第 6 行),试图读取信道 ch。由于没有 Go 协程向该信道写入数据,因此 select 语句会一直阻塞,导致死锁。该程序会触发运行时 panic,报错信息如下:

    fatal error: all goroutines are asleep - deadlock!
    
    goroutine 1 [chan receive]:  
    main.main()  
        /tmp/sandbox416567824/main.go:6 +0x80

    如果存在默认情况,就不会发生死锁,因为在没有其他 case 准备就绪时,会执行默认情况。我们用默认情况重写后,程序如下:

    package main
    
    import "fmt"
    
    func main() {  
        ch := make(chan string)    
        select {    
            case <-ch:    
            default:
                fmt.Println("default case executed")
        }
    }  

    以上程序会输出:

    default case executed

    如果 select 只含有值为 nil 的信道,也同样会执行默认情况。

    package main
    
    import "fmt"
    
    func main() {  
        var ch chan string
        select {    
            case v := <-ch:
                fmt.Println("received value", v)    
            default:
                fmt.Println("default case executed")
    
        }
    }

    在线运行程序

    在上面程序中,ch 等于 nil,而我们试图在 select 中读取 ch(第 8 行)。如果没有默认情况,select 会一直阻塞,导致死锁。由于我们在 select内部加入了默认情况,程序会执行它,并输出:

    default case executed

    空 select

    package main
    func main() {  
        select {}
    }

    我们已经知道,除非有 case 执行,select 语句就会一直阻塞着。在这里,select 语句没有任何 case,因此它会一直阻塞,导致死锁。该程序会触发 panic,输出如下:

    fatal error: all goroutines are asleep - deadlock!
    
    goroutine 1 [select (no cases)]:  
    main.main()  
        /tmp/sandbox299546399/main.go:4 +0x20
  • 相关阅读:
    九宫格小游戏源码分享
    DeviceOne 竟然做出来如此复杂的App
    DeviceOne 让你一见钟情的App快速开发平台
    MySQL初始化
    MySQL的操作
    MySQL
    Library
    Python模块
    Anaconda的使用
    面向对象之成员修饰 特殊成员 methclass
  • 原文地址:https://www.cnblogs.com/-wenli/p/11824932.html
Copyright © 2011-2022 走看看