戈朗:为什么os.Exit不';不要在goroutines内工作

戈朗:为什么os.Exit不';不要在goroutines内工作,go,goroutine,Go,Goroutine,我有一个非常简单的算法研究计划。当成功来临时,goroutine应该通过os.Exit(0)关闭(结束)。我等了一天,两天。。。。什么?:) 下面是简单的代码 package main import "os" func main() { for { go func() { os.Exit(0) }() } } 我的问题是: 为什么os.Exit不能终止goroutine 终止(停止)goroutine执行的正确方法是什么 游乐场:您可以通过从函数返回来终止g

我有一个非常简单的算法研究计划。当成功来临时,goroutine应该通过os.Exit(0)关闭(结束)。我等了一天,两天。。。。什么?:)

下面是简单的代码

package main

import "os"

func main() {
    for {
        go func() { os.Exit(0) }()
    }
}
我的问题是:

  • 为什么os.Exit不能终止goroutine
  • 终止(停止)goroutine执行的正确方法是什么

  • 游乐场:

    您可以通过从函数返回来终止goroutine。如果需要确保goroutine运行到完成,则需要等待goroutine完成。这通常通过
    sync.WaitGroup
    或通过通道同步goroutines来完成

    在您的示例中,首先需要确保不可能产生无限多的goroutine。因为在
    main
    和新的goroutine之间没有同步点,所以不能保证它们中的任何一个会在主循环运行时执行
    os.Exit
    调用

    等待任意数量的goroutine完成的通常方法是使用
    sync.WaitGroup
    ,这将确保它们在退出
    main
    之前都已执行

    wg := sync.WaitGroup{}
    for i := 0; i < 10000; i++ {
        wg.Add(1)
        go func() { defer wg.Done() }()
    }
    
    wg.Wait()
    fmt.Println("done")
    
    wg:=sync.WaitGroup{}
    对于i:=0;i<10000;i++{
    工作组.添加(1)
    go func(){defer wg.Done()}()
    }
    wg.Wait()
    fmt.Println(“完成”)
    
    您遇到了Go计划程序的一个棘手的角落。答案是,
    os.Exit
    确实会导致整个进程退出,但按照您的方式,goroutines从未运行过

    可能发生的情况是for循环不断将新的goroutine添加到可用goroutine列表中,但由于整个过程仅在一个OS线程中运行,Go调度器从未真正安排不同的goroutine,只是继续运行for循环,而没有运行任何您生成的goroutine。请尝试以下方法:

    package main
    
    import "os"
    
    func main() {
        for {
            go func() { os.Exit(0) }()
            func() {}()
        }
    }
    
    如果你在运动场上运行它,它应该会工作(事实上)

    好吧,上面的代码可以工作,而你的代码却不能工作,这一事实应该是相当神秘的。这样做的原因是Go调度程序实际上是非抢占的。这意味着,除非一个给定的goroutine自愿决定给调度器运行其他东西的选项,否则其他任何东西都不会运行

    现在很明显,您从未编写过包含命令的代码,以使调度程序有机会运行。在编译代码时,Go编译器会自动将这些代码插入到代码中。下面是上述代码工作的关键:goroutine可能决定运行调度程序的一个时间是调用函数的时间。因此,通过添加
    func(){}()
    调用(这显然不起任何作用),我们允许编译器添加对调度程序的调用,从而使代码有机会调度不同的goroutine。因此,其中一个派生的goroutine运行,调用
    os.Exit
    ,进程退出


    编辑:如果编译器内联调用,函数调用本身可能不够充分(或者,在本例中,由于函数调用不执行任何操作,因此将其完全删除),另一方面,保证工作。

    执行死手或压井开关

    package main
    
    import (
            "fmt"
            "time"
            "os"
    )
    
    const maxNoTickle = 50          // will bail out after this many no tickles
    const maxWorking = 20           // pretendWork() will tickle this many times
    const deadTicks = 250           // milliseconds for deadHand() to check for tickles
    const reportTickles = 4         // consecutive tickles or no tickles to print something
    
    var (
            tickleMe bool           // tell deadHand() we're still alive
            countNoTickle int       // consecutive no tickles
            countGotTickle int      // consecutive tickles
    )
    
    /**
    *       deadHand() - callback to kill program if nobody checks in after some period
    */
    func deadHand() {
            if !tickleMe {
                    countNoTickle++
                    countGotTickle = 0
                    if countNoTickle > maxNoTickle {
                            fmt.Println("No tickle max time reached. Bailing out!")
                            // panic("No real panic. Just checking stack")
                            os.Exit(0)
                    }
                    if countNoTickle % reportTickles == 0 {
                            // print dot for consecutive no tickles
                            fmt.Printf(".")
                    }
            } else {
                    countNoTickle = 0
                    countGotTickle++
                    tickleMe = false        // FIXME: might have race condition here
                    if countGotTickle % reportTickles == 0 {
                            // print tilda for consecutive tickles
                            fmt.Printf("~")
                    }
            }
            // call ourselves again
            time.AfterFunc(deadTicks * time.Millisecond, deadHand)
    }
    
    /**
    *       init() - required to start deadHand
    */
    func init() {
            time.AfterFunc(250 * time.Millisecond, deadHand)
            tickleMe = true
    }
    
    /**
    *       pretendWork() - your stuff that does its thing
    */
    func pretendWork() {
            for count := 0; count < maxWorking; count++ {
                    tickleMe = true // FIXME: might have race condition here
                    // print W pretending to be busy
                    fmt.Printf("W")
                    time.Sleep(100 * time.Millisecond)
            }
    }
    
    func main() {
            go workTillDone()
            for {
                    // oops, program went loop-d-loopy
            }
    }
    
    主程序包
    进口(
    “fmt”
    “时间”
    “操作系统”
    )
    const maxNoTickle=50//将在多次无痒之后退出
    const maxWorking=20//invokeWork()将多次挠痒痒
    const deadTicks=250//毫秒,用于deadHand()检查痒
    const reportTickles=4//连续挠痒或没有挠痒来打印某物
    变量(
    tickleMe bool//告诉deadHand()我们还活着
    countNoTickle int//连续无痒
    countGotTickle int//连续挠痒
    )
    /**
    *deadHand()-如果一段时间后没有人签入,则回调以终止程序
    */
    func deadHand(){
    如果!痒{
    倒计时++
    countGotTickle=0
    如果countNoTickle>maxNoTickle{
    fmt.Println(“达到无痒最长时间。退出!”)
    //恐慌(“没有真正的恐慌,只是检查堆栈”)
    操作系统退出(0)
    }
    如果countNoTickle%reportTickles==0{
    //打印圆点,连续无痒感
    格式打印F(“.”)
    }
    }否则{
    计数=0
    数痒++
    tickleMe=false//FIXME:这里可能有竞争条件
    如果CountGoTickle%reportTickles==0{
    //打印tilda以连续挠痒痒
    格式打印F(“~”)
    }
    }
    //再打电话给我们自己
    time.AfterFunc(死区信号*时间毫秒,死区信号)
    }
    /**
    *init()-需要立即启动
    */
    func init(){
    time.AfterFunc(250*时间毫秒,死手)
    挠痒痒=真
    }
    /**
    *假装工作()-你的东西做它的事
    */
    func工作(){
    对于计数:=0;计数
    你确定这就是原因吗?因为给定的代码对我来说很好。在任何情况下,您是否尝试过simple
    return
    ?我不知道您在哪里尝试过,但在我的计算机上,此代码没有完成。我不知道如何在goroutines中使用
    return
    。goroutine中的所有计算和结果都不能是“返回”。
    go func(){return}()
    ?我知道(手动计算后)会丢失成功结果。。。因为操作系统