Go语言并发之道学习九 心跳

并发进程检测-心跳

package main

import (
	"math/rand"
	"time"
	"fmt"
)

/*
并发进程取消的原因:
·超时:超时是隐式取消

·用户干预:为了获取良好的用户体验,通常建议维持一个长连接,然后以轮询间隔将状态报告发送给用户,或允许用户查看他们认为合适的状态。
当用户使用并发程序时,有时需要允许用户取消他们已经开始的操作。

·父进程取消:对于这个问题,如果任何一种并发操作的父进程停止,那么子进程也将被取消。

·复制请求:我们可能希望将数据发送到多个并发进程,以尝试从其中一个进程获得更快的响应。
当第一个响应回来的时候,我们将会取消其余的进程。
*/

func main() {
	fmt.Println("testheart")
	testheart()
	fmt.Println()
	fmt.Println("testheart2")
	testheart2()
}

//心跳
func testheart(){
	doWork := func(
		done <-chan interface{},
		pulseInterval time.Duration,
		) (<-chan interface{},<-chan time.Time) {
		heartbeat := make(chan interface{})
		results := make(chan time.Time)
		go func() {
			defer close(heartbeat)
			defer close(results)

			pulse := time.Tick(pulseInterval)
			workGen := time.Tick(2*pulseInterval)

			sendPulse := func() {
				select {
				case heartbeat <- struct {}{}:
				default:
				}
			}
			sendResult := func(r time.Time) {
				for {
					select {
					case <-done:
						return
					case <-pulse:
						sendPulse()
					case results<-r:
						return
					}
				}
			}
			for {
				select {
				case <- done:
					return
				case <-pulse:
					sendPulse()
				case r := <-workGen:
					sendResult(r)
				}
			}
		}()
		return heartbeat,results
	}
	/*
	·heartbeat := make(chan interface{})
	我们建立了一个发送心跳的channel ,我们把这个返回给 doWork。

	·pulse := time.Tick(pulseInterval)
	我们设定心跳的间隔时间为我们接到的pulseInterval ,每隔一个pulseInterval 的时长都会有一些东西读取这个channel

	·workGen := time.Tick(2*pulseInterval)
	这是另一个用来模拟滴答声的channel ,我们选择的持续时间大于pulseInterval ,这样我们就能看到从goroutine中发出的一些心跳。

	·case heartbeat <- struct {}{}:
	  default:
	注意,我们在这里加入了一个默认语句,我们必须时刻警惕这样一个事实:
	可能会没有人接收我们的心跳,从goroutine 发出的信息是重要的,但心跳却不一定重要。

	·case <-pulse:
	  sendPulse()
	就像done channel 一样,当你执行发送或接收时,你也需要包含一个发送心跳的分支。

	*/

	done := make(chan interface{})
	time.AfterFunc(10*time.Second, func() {close(done)})

	const timeout = 2*time.Second
	heartbeat,results := doWork(done,timeout/2)
	for {
		select {
		case _,ok := <-heartbeat:
			if ok == false{
				return
			}
			fmt.Println("pulse")
		case r,ok := <-results:
			if ok == false{
				return
			}
			fmt.Printf("results %v\n", r.Second())
		case <-time.After(timeout):
			return
		}
	}

	/*
	·time.AfterFunc(10*time.Second, func() {close(done)})
	我们声明了一个标准的done channel ,并在10秒后关闭,这给我们的goroutine 做一些工作的时间

	·const timeout = 2*time.Second
	这里我们设置了超时时间,我们使用此方法将心跳间隔与超时时间联系起来

	·heartbeat,results := doWork(done,timeout/2)
	我们在这里timeout/2 ,这使得我们的心跳有额外的响应时间,以便我们的超时有一定缓冲时间。

	·case _,ok := <-heartbeat:
	在这里,我们处理心跳,当没有消息时,我们至少知道每过timeout/2 的时间会从心跳channel 发出一条消息。
	如果我们什么都没有收到,我们便知道是goroutine 本身出了问题。

	·case r,ok := <-results:
	在这里,我们处理results channel

	·case <-time.After(timeout):
	如果我们没有收到心跳或其他消息,就会超时

	*/
}

func testheart2(){
	doWork := func(
		done <-chan interface{},
		) (<-chan interface{},<-chan int){
		heartbeatStream := make(chan interface{},1)
		workStream := make(chan int)
		go func() {
			defer close(heartbeatStream)
			defer close(workStream)

			for i:=0;i < 10;i++{
				select {
				case heartbeatStream<- struct {}{}:
				default:
				}

				select {
				case <-done:
					return
				case workStream<-rand.Intn(10):
				}
			}
		}()
		return heartbeatStream,workStream
	}

	done:=make(chan interface{})
	defer close(done)

	heartbeat,results := doWork(done)
	for{
		select {
		case _,ok := <-heartbeat:
			if ok {
				fmt.Println("pulse")
			} else {
				return
			}
		case r,ok := <-results:
			if ok {
				fmt.Printf("results %v\n", r)
			}else{
				return
			}
		}
	}

	/*
	·heartbeatStream := make(chan interface{},1)
	在这里,我们创建一个缓冲区大小为1的 heartbeat channel ,这确保了即使没有及时接受发送的消息,至少也会发送一个心跳。

	·for i:=0;i < 10;i++{
	  select {
	在这里,我们为心跳设置了一个单独的select块,我们希望将发送results 和心跳分开,
	因为如果接受者没有准备好接收结果,作为替代,他将接收到一个心跳,而代表当前结果的值将会丢失。
	由于我们有默认逻辑,所以这里也没有包含对done channel 的处理。

	·default:
	在这里,为了防止没人接收我们的心跳,我们增加了默认逻辑,
	因为我们的heartbeat channel 创建时有一个缓冲区那么大,所以如果有人正在监听,
	但是没有及时收到第一个心跳,接收者仍然可以接收到心跳。


	*/
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
本书作者带你一步一步深入这些方法。你将理解 Go语言为何选定这些并发模型,这些模型又会带来什么问题,以及你如何组合利用这些模型中的原语去解决问题。学习那些让你在独立且自信的编写与实现任何规模并发系统时所需要用到的技巧和工具。 理解Go语言如何解决并发难以编写正确这一根本问题。 学习并发与并行的关键性区别。 深入到Go语言的内存同步原语。 利用这些模式中的原语编写可维护的并发代码。 将模式组合成为一系列的实践,使你能够编写大规模的分布式系统。 学习 goroutine 背后的复杂性,以及Go语言的运行时如何将所有东西连接在一起。 作者简介 · · · · · · Katherine Cox-Buday是一名计算机科学家,目前工作于 Simple online banking。她的业余爱好包括软件工程、创作、Go 语言(igo、baduk、weiquei) 以及音乐,这些都是她长期的追求,并且有着不同层面的贡献。 目录 · · · · · · 前言 1 第1章 并发概述 9 摩尔定律,Web Scale和我们所陷入的混乱 10 为什么并发很难? 12 竞争条件 13 原子性 15 内存访问同步 17 死锁、活锁和饥饿 20 确定并发安全 28 面对复杂性的简单性 31 第2章 对你的代码建模:通信顺序进程 33 并发与并行的区别 33 什么是CSP 37 如何帮助你 40 Go语言并发哲学 43 第3章 Go语言并发组件 47 goroutine 47 sync包 58 WaitGroup 58 互斥锁和读写锁 60 cond 64 once 69 池 71 channel 76 select 语句 92 GOMAXPROCS控制 97 小结 98 第4章 Go语言并发模式 99 约束 99 for-select循环103 防止goroutine泄漏 104 or-channel 109 错误处理112 pipeline 116 构建pipeline的最佳实践 120 一些便利的生成器 126 扇入,扇出 132 or-done-channel 137 tee-channel 139 桥接channel模式 140 队列排队143 context包 151 小结 168 第5章 大规模并发 169 异常传递169 超时和取消 178 心跳 184 复制请求197 速率限制199 治愈异常的goroutine 215 小结 222 第6章 goroutine和Go语言运行时 223 工作窃取223 窃取任务还是续体 231 向开发人员展示所有这些信息 240 尾声 240 附录A 241

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值