Golang 退出 goroutine的几种方式

传统方式

在刚开始学go的时候,没用过Context包,那么退出携程的方式一般有这么几种

使用携 chan 发送消息通知,这种一般只适合单个goroutine

func exit01() {
    done := make(chan bool)
    go func() {
        for {
            select {
            case <-done:
                fmt.Println("退出携程")
                return
            default:
                fmt.Println("监控中...")
                time.Sleep(1 * time.Second)
            }
        }
    }()
    time.Sleep(3 * time.Second)
    done <- true
    time.Sleep(5 * time.Second)
    fmt.Println("程序退出")
}

使用关闭 chan 的方式通知多个goroutine退出

func exit02() {
    done :=make(chan bool)
    go func() {
        for{
            select {
            case <-done:
                fmt.Println("退出携程01")
                return
            default:
                fmt.Println("监控01...")
                time.Sleep(1*time.Second)
            }
        }
    }()

    go func() {
        for res :=range done{ //没有消息阻塞状态,chan关闭 for 循环结束
            fmt.Println(res)
        }
        fmt.Println("退出监控03")
    }()

    go func() {
        for{
            select {
            case <-done:
                fmt.Println("退出携程02")
                return
            default:
                fmt.Println("监控02...")
                time.Sleep(1*time.Second)
            }
        }
    }()
    time.Sleep(3*time.Second)
    close(done)
    time.Sleep(5*time.Second)
    fmt.Println("退出程序")
}

初识 Context包

一个用于手动控制 goroutine 退出或者结束

获取 context上下文两种方式

ctx := context.Background() //这只能用于高等级(在 main 或顶级请求处理中)。这能用于派生我们稍后谈及的其他 context

ctx := context.TODO()  // 也只能用于高等级或当您不确定使用什么 context,或函数以后会更新以便接收一个 context 

他们的底层实现完全一致,不同的是,静态分析工具可以使用它来验证 context 是否正确传递,
这是一个重要的细节,因为静态分析工具可以帮助在早期发现潜在的错误,并且可以连接到 CI/CD 管道

var (
    background = new(emptyCtx)
    todo       = new(emptyCtx)
)

使用context.WithTimeout,主动调用 cancel()方法,可以在时间超时之前退出 goroutine

func exit03() {
    ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
    go func() {
        for{
            select {
            case <-ctx.Done():
                fmt.Println("退出携程")
                return
            default:
                fmt.Println("请求中..")
                time.Sleep(1*time.Second)
            }
        }
    }()
    time.Sleep(5*time.Second)
    //cancel() //也可以手动调用 cancel()方法退出
    //time.Sleep(2*time.Second)
    fmt.Println("程序退出")

}

使用 context.WithCanel()方法,根据外部条件手动调用 cancel()方法退出
只有创建它的函数才能调用取消函数来取消此 context。如果您愿意,可以传递取消函数,但是,强烈建议不要这样做。
这可能导致取消函数的调用者没有意识到取消 context 的下游影响。可能存在源自此的其他 context,
这可能导致程序以意外的方式运行。简而言之,永远不要传递取消函数

func exit04() {
    ctx, cancel := context.WithCancel(context.Background())
    go func() {
        for{
            select {
            case <-ctx.Done():
                fmt.Println("退出携程")
                return
            default:
                fmt.Println("监控01")
                time.Sleep(1*time.Second)
            }
        }
    }()

    time.Sleep(5*time.Second)
    cancel()
    time.Sleep(2*time.Second)
    fmt.Println("退出程序")

}   

使用 context.WithDeadLine() ,在指定的时间退出 goroutine

func exit05() {
    stringTime := "2019-08-11 09:08:01"
    loc, _ := time.LoadLocation("Local")
    the_time, _ := time.ParseInLocation("2006-01-02 15:04:05", stringTime, loc)
    ctx, _ := context.WithDeadline(context.Background(), the_time)
    go func() {
        for{
            select {
            case <-ctx.Done():
                fmt.Println("退出 goroutine")
                return
            default:
                fmt.Println("监控...")
                time.Sleep(1*time.Second)
            }
        }
    }()

    time.Sleep(60*time.Second)
    fmt.Println("程序退出")

}

使用context.WithValue()传值,在所有的context树中都能获取到该值,如果设置相同的key 则覆盖该值
不建议使用 context 值传递关键参数,而是函数应接收签名中的那些值,使其显式化。

func exit06() {
    ctx := context.WithValue(context.Background(), "msg", "hello word")
    go func(ctx context.Context) {
        fmt.Println(ctx.Value("msg"))
    }(ctx)
    time.Sleep(2*time.Second)
    fmt.Println("程序退出")
}

相关建议约束

  • context.Background 只应用在最高等级,作为所有派生 context 的根。
  • context.TODO 应用在不确定要使用什么的地方,或者当前函数以后会更新以便使用 context。
  • context 取消是建议性的,这些函数可能需要一些时间来清理和退出。
  • context.Value 应该很少使用,它不应该被用来传递可选参数。这使得 API 隐式的并且可以引起错误。取而代之的是,这些值应该作为参数传递。
  • 不要将 context 存储在结构中,在函数中显式传递它们,最好是作为第一个参数。
  • 永远不要传递不存在的 context 。相反,如果您不确定使用什么,使用一个 ToDo context。
  • Context 结构没有取消方法,因为只有派生 context 的函数才应该取消 context。

参考:https://studygolang.com/articles/13866?fr=sidebar

原文地址:https://www.cnblogs.com/nirao/p/11333990.html

时间: 2024-08-08 07:44:49

Golang 退出 goroutine的几种方式的相关文章

iOS 开发之 - 关闭键盘 退出键盘 的5种方式

iOS 开发之 - 关闭键盘 退出键盘 的5种方式 1.点击编辑区以外的地方(UIView) 2.点击编辑区域以外的地方(UIControl) 3.使用制作收起键盘的按钮 4.使用判断输入字元 5.关于键盘遮蔽的问题 1,点击编辑区以外的地方(UIView) 这是一种很直觉的方法,当不再需要使用虚拟键盘时,只要点击虚拟键盘和编辑区域外的地方,就可以将键盘收起,下面程式码是在 UIView 中内建的触碰事件方法函式,您可以参考 Touch Panel / 触碰萤幕 / 压力感应器的基本使用方式一文

IOS 关闭键盘 退出键盘 的5种方式(转)

IOS 关闭键盘 退出键盘 的5种方式 转自“http://blog.csdn.net/w88193363/article/details/24423635” 分类: iphone2014-04-24 17:03 2197人阅读 评论(0) 收藏 举报 1.点击编辑区以外的地方(UIView) 2.点击编辑区域以外的地方(UIControl) 3.使用制作收起键盘的按钮 4.使用判断输入字元 5.关于键盘遮蔽的问题 1,点击编辑区以外的地方(UIView) 这是一种很直觉的方法,当不再需要使用虚

退出应用的2种方式

实现退出方式要activity的 boolean onKeyDown(int keyCode, KeyEvent event) 方法里实现 直接见代码吧: 方法一: @Override public boolean onKeyDown(int keyCode, KeyEvent event) { //退出方法一 if(keyCode == KeyEvent.KEYCODE_BACK){ AlertDialog.Builder dialog = new AlertDialog.Builder(th

iOS退出键盘的两种方式

1,可以让叫出键盘的那个控件,退出第一响应者; 例如:textField叫出的键盘,那么就可以调用textField.resignFirstResponder,达到谁叫出的键盘谁退回去 2,可以让叫出键盘的那个控件的父类控件退出键盘: 例如,textField叫出的键盘,它的父类控件是View,那么就调用:[View endEditing:YES]

golang中四种方式实现子goroutine与主协程的同步

如何实现子goroutine与主线程的同步 第一种方式:time.sleep(),这种方式很太死板,就不演示了. 第二种方式:使用channel机制,每个goroutine传一个channel进去然后往里写数据,在再主线程中读取这些channel,直到全部读到数据了子goroutine也就全部运行完了,那么主goroutine也就可以结束了.这种模式是子线程去通知主线程结束. package main import ( "fmt" ) func main() { var chanTes

C#退出的几种方式

1.this.Close();   只是关闭当前窗口,若不是主窗体的话,是无法退出程序的,另外若有托管线程(非主线程),也无法干净地退出: 2.Application.Exit();  强制所有消息中止,退出所有的窗体,但是若有托管线程(非主线程),也无法干净地退出: 3.Application.ExitThread(); 强制中止调用线程上的所有消息,同样面临其它线程无法正确退出的问题: 4.System.Environment.Exit(0);   这是最彻底的退出方式,不管什么线程都被强制

返回键捕获 应用程序退出的两种方式(转)

作为应用程序一个较好的用户体验应该是:在退出应用程序前询问用户是否真正退出?目前普遍做法是,一在退出前询问是否真正退出,二是连续按两下退出. 返回键捕获 应用程序退出的两种方式 实现上述两种应用退出方式需要在onkeyDown()来进行捕获返回键 在Activity中,如果需要动态注册广播官方文档建议在onResume()进行注册,在onPause()中取消注册.注册部分代码如下: 应用程序退出方式一:确认退出对话框 public boolean onKeyDown(int keyCode, K

Golang开发环境搭建(Notepad++、LiteIDE两种方式以及martini框架使用)

本文介绍两种Golang的开发环境一种基于notepad++.另一种基于liteide. 1.下载Golang语言的pkg:http://golangtc.com/download 直接点击安装,一路next. 2.程序员必备神器notepad++开发Golang环境很简单 一次点击:插件->Plugin Manger->Show Plugin Manger,安装插件GOnpp,重启notepad++. 新建文件命名为hello.go用notepad++打开,拷贝如下代码: package m

winform中关闭退出和打开新窗口的几种方式

关闭窗口,退出的两种方式:1.this.Close();//关闭当前窗口2.Application.Exit();//关闭所有窗口,退出应用程序 打开窗口的三种方式:1.从一个窗口打开另一个窗口新窗口对象.show()2.从一个窗口打开另一个对话框新窗口对象.showDialog()3.在一个窗口内部打开另一个窗口(MDI父窗体) 1)先设置父窗口属性IsMdiContainer 为True 2)new 一个新窗口对象后,新窗口对象.MdiParent = this; 3)新窗口对象.Show(