zoukankan      html  css  js  c++  java
  • golang学习笔记---channel(2)

    channel容量为0和为1的区别

    • 容量为1的channel是有缓冲channel的特殊情况,可以用在2个goroutine之间同步状态,或者其中一个等待另一个完成时才继续执行任务的情况。
    • 无缓存的channel的容量始终为0,发送者发送数据和接受者接受数据时同时的,无任何中间态,不能缓冲任何数据。
    • 容量为1的channel是可以缓冲1个数据,发送者和接受者之间可以不同时进行,可以发送者可以先把数据放进去,接受者可以过会儿再读取数据。无缓存的 channel 的发送者和接受者是相互等待,发送者等待接受者准备就绪才能发送数据,接受者等待发送者准备就绪才能接受数据,如果无缓存的 channel 在同一个协程中既发送又接受就会造成死锁而报错。

    使用Range来遍历channel
    使用for range来遍历channel,会自动等待channel的操作,一直到channel被关闭,退出循环。
    第一个协程发送完数据之后关闭channel,使用range遍历读取channel中的数据,当channel被关闭后会退出循环结束程序

    package main
    
    import (
    	"fmt"
    	"time"
    )
    
    func main() {
    	//缓冲容量为3的channel
    	c := make(chan int64, 3)
    	//在协程中向channel中写数据
    	go func() {
    		for i := 0; i < 10; i++ {
    			time.Sleep(time.Microsecond * 100)
    			c <- time.Now().UnixNano()
    		}
    		close(c)
    	}()
    
    	//通过range来打印数据,直到channel被关闭
    	go func() {
    
    		for i := range c {
    			time.Sleep(time.Microsecond * 1000)
    			fmt.Println(i)
    		}
    	}()
    
    	fmt.Scanln() //使用了fmt.Scanln()通过控制台输入扫描来hold住控制台,不让程序退出
    	fmt.Println("完成")
    }
    

      

    关闭Channel
    关闭channel使用了内建的函数close,对于关闭channel需要注意如下几点:

    • 如果向已经关闭的channel写数据就会导致panic: send on closed channel
    • 从已经关闭的channel中读取数据是不会导致panic的,可以继续读取已经发送的数据,但如果已经发送的数据读取完成时继续读取,就会会读取到类型默认值或者零值;
    • 如果通过range读取数据,channel关闭后就会跳出for循环;
    • 如果重复再关闭已经关闭的channel,也会导致panic。

    select

    • select 可以等待和处理多个通道。使用select可以在case语句中选择一组channel中未阻塞的channel。
    • select只会执行一次不会循环,只会选择一个case来处理,如果要一直处理channel,通常要结合一个无限for循环一起来使用
    • 在default case存在的情况下,如果没有case需要处理,则会选择default去处理;如果没有default case,则select语句会阻塞,直到某个case需要处理。
    • 如果在使用for 无限循环+select来操作多个channel,当channel被关闭后,会一直读取类型默认值,这样会导致进入无限死循环
    package main
    
    import (
    	"fmt"
    	"time"
    )
    
    func main() {
    	//创建2个channel
    	c1 := make(chan string)
    	c2 := make(chan string)
    	//通过2个协程分别往2个channel中写数据
    	go func() {
    		for i := 0; i < 9; i++ {
    			time.Sleep(100 * time.Millisecond)
    			c1 <- fmt.Sprintf("c1: %d", i+1)
    		}
    		close(c1)
    
    	}()
    	go func() {
    		for i := 0; i < 5; i++ {
    			time.Sleep(100 * time.Millisecond)
    			c2 <- fmt.Sprintf("c2: %d", i+1)
    		}
    		close(c2)
    	}()
    	//通过1个协程从2个channel中读取数据,如果没有数据则阻塞
    	go func() {
    		for {
    			select { // 死循环
    			case msg1 := <-c1:
    				fmt.Printf("received %d: %s 
    ", time.Now().Unix(), msg1)
    			case msg2 := <-c2:
    				fmt.Printf("received %d: %s 
    ", time.Now().Unix(), msg2)
    			}
    		}
    	}()
    
    	fmt.Scanln()
    
    }
    

     对于读取已经关闭的channel时,可以使用返回值来判断channel是否被关闭,下面的例子中如果返回的ok为false,就说明channel已经被关闭: 

    Select超时
    在select中可以处理超时,超时在处理外部资源或需要绑定执行时间的程序非常重要,通过channel和select,在Go中可以很容易且优雅的实现超时机制。在下面的例子使用一个协程来模拟任务处理,利用sleep 5秒钟来模拟任务执行时间,任务完成后向channel中写入结果;在select语句中实现超时,第一个case来读取channel中的数据,等待结果写入;第二个channel中使用time.After(3 * time.Second)来等待3秒超时时间。由于实际任务执行时间是5秒钟,超时时间是3秒钟,所以等待3秒钟后,time.After返回的channel中会写入一个时间,select语句就选择第二个case执行,然后结束程序:

    package main
    
    import "time"
    import "fmt"
    
    func main() {
    
    	c1 := make(chan string, 1)
    	go func() {
    		fmt.Println("开始时间", time.Now().Unix())
    		time.Sleep(5 * time.Second)
    		c1 <- "result 1"
    	}()
    
    	select {
    	case res := <-c1:
    		fmt.Println(res)
    	case <-time.After(3 * time.Second):
    		fmt.Println("timeout 3")
    	}
    	fmt.Println("完成时间:", time.Now().Unix())
    
    }

     输出:

    开始时间 1595297994

    timeout 3

    完成时间: 1595297997

  • 相关阅读:
    Oracle 推出 ODAC for Entity Framework 和 LINQ to Entities Beta版
    Entity Framework Feature CTP 5系列文章
    MonoDroid相关资源
    MSDN杂志上的Windows Phone相关文章
    微软学Android Market推出 Web Windows Phone Marketplace
    使用 Visual Studio Agent 2010 进行负载压力测试的安装指南
    MonoMac 1.0正式发布
    Shawn Wildermuth的《Architecting WP7 》系列文章
    使用.NET Mobile API即51Degrees.mobi检测UserAgent
    MongoDB 客户端 MongoVue
  • 原文地址:https://www.cnblogs.com/saryli/p/13353271.html
Copyright © 2011-2022 走看看