ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

[Golang]-6 超时处理、非阻塞通道操作、通道的关闭和遍历

2020-12-17 02:02:01  阅读:244  来源: 互联网

标签:received jobs Golang job 超时 sent 通道


目录

超时处理

超时 对于一个连接外部资源,或者其它一些需要花费执行时间的操作的程序而言是很重要的。
得益于通道和 select,在 Go中实现超时操作是简洁而优雅的。

import (
	"fmt"
	"time"
)

// 例子中,假如我们执行一个外部调用,并在 2 秒后通过通道 c1 返回它的执行结果。
func main() {

	c1 := make(chan string, 1)
	go func() {
		time.Sleep(time.Second * 2)
		c1 <- "result 1"
	}()

	// 这里是使用 select 实现一个超时操作。
	// res := <- c1 等待结果,<-Time.After 等待超时时间 1 秒后发送的值。
	// 由于 select 默认处理第一个已准备好的接收操作,如果这个操作超过了允许的 1 秒的话,将会执行超时 case。
	select {
	case res := <-c1:
		fmt.Println(res)
	case <-time.After(time.Second * 1):
		fmt.Println("timeout 1")
	}

	// 如果我允许一个长一点的超时时间 3 秒,将会成功的从 c2接收到值,并且打印出结果。
	c2 := make(chan string, 1)
	go func() {
		time.Sleep(time.Second * 2)
		c2 <- "result 2"
	}()
	select {
	case res := <-c2:
		fmt.Println(res)
	case <-time.After(time.Second * 3):
		fmt.Println("timeout 2")
	}
}

输出

timeout 1
result 2

运行这个程序,首先显示运行超时的操作,然后是成功接收的。

使用这个 select 超时方式,需要使用通道传递结果。

非阻塞通道操作

常规的通过通道发送和接收数据是阻塞的。
然而,我们可以使用带一个 default 子句的 select 来实现非阻塞 的发送、接收,甚至是非阻塞的多路 select。

func main() {
	messages := make(chan string)
	signals := make(chan bool)

	// 这里是一个非阻塞接收的例子。如
	// 果在 messages 中存在,然后 select 将这个值带入 <-messages case中。
	// 如果不是,就直接到 default 分支中。
	select {
	case msg := <-messages:
		fmt.Println("received message ", msg)
	default:
		fmt.Println("no message received")
	}

	// 一个非阻塞发送的实现方法和上面一样。
	msg := "hi"
	select {
	case messages <- msg:
		fmt.Println("sent message", msg)
	default:
		fmt.Println("no message sent")
	}

	// 我们可以在 default 前使用多个 case 子句来实现一个多路的非阻塞的选择器。
	// 这里我们试图在 messages和 signals 上同时使用非阻塞的接受操作。
	select {
	case msg := <-messages:
		fmt.Println("received message", msg)
	case sig := <-signals:
		fmt.Println("received signal", sig)
	default:
		fmt.Println("no activity")
	}
}

输出

no message received
no message sent
no activity

通道的关闭

关闭 一个通道意味着不能再向这个通道发送值了。这个特性可以用来给这个通道的接收方传达工作已经完成的信息。

import (
	"fmt"
	"time"
)

// 在这个例子中,我们将使用一个 jobs 通道来传递 main() 中 Go协程任务执行的结束信息到一个工作 Go 协程中。
// 当我们没有多余的任务给这个工作 Go 协程时,我们将 close 这个 jobs 通道。
func main() {
	jobs := make(chan int, 5)
	done := make(chan bool)

	// 这是工作 Go 协程。使用 j, more := <- jobs 循环的从jobs 接收数据。
	// 在接收的这个特殊的二值形式的值中,如果 jobs 已经关闭了,并且通道中所有的值都已经接收完毕,那么 more 的值将是 false。
	// 当我们完成所有的任务时,将使用这个特性通过 done 通道去进行通知。
	go func() {
		for {
			j, more := <-jobs
			if more {
				fmt.Println("received job", j)
			} else {
				fmt.Println("received all jobs")
				done <- true
				return
			}
		}
	}()

	// 这里使用 jobs 发送 3 个任务到工作函数中,然后关闭 jobs。
	for j := 1; j <= 3; j++ {
		jobs <- j
		fmt.Println("sent job", j)
		time.Sleep(100)
	}
	close(jobs)
	fmt.Println("sent all jobs")

	// 我们使用前面学到的通道同步方法等待任务结束。
	<-done
}

输出

sent job 1
received job 1
sent job 2
received job 2
sent job 3
received job 3
sent all jobs
received all jobs

如果去掉 time.Sleep(100) 这句

sent job 1
sent job 2
sent job 3
received job 1
received job 2
received job 3
sent all jobs
received all jobs

通道遍历

for 和 range为基本的数据结构提供了迭代的功能。我们也可以使用这个语法来遍历从通道中取得的值。

func main() {
	// 我们将遍历在 queue 通道中的两个值。
    queue := make(chan string, 2)
    queue <- "one"
    queue <- "two"
    close(queue)

	// 这个 range 迭代从 queue 中得到的每个值。
	// 因为我们在前面 close 了这个通道,这个迭代会在接收完 2 个值之后结束。
	// 如果我们没有 close 它,我们将在这个循环中继续阻塞执行,等待接收第三个值
    for elem := range queue {
        fmt.Println(elem)
    }
}

输出

one
two

一个非空的通道也是可以关闭的,但是通道中剩下的值仍然可以被接收到。

标签:received,jobs,Golang,job,超时,sent,通道
来源: https://www.cnblogs.com/feily/p/14147617.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有