如何保持长时间运行的Go程序,运行?

Nat*_*ate 41 daemon go goroutine

我用Go编写了一个长期运行的服务器.Main触发了程序逻辑执行的几个goroutine.之后主要没有任何用处.一旦主要退出,程序将退出.我现在用来保持程序运行的方法只是对fmt.Scanln()的简单调用.我想知道别人如何保持主力退出.以下是一个基本的例子.这里可以使用哪些想法或最佳实践?

我考虑通过在所述频道上接收来创建一个频道并延迟主要的退出,但我认为如果我的所有goroutine在某些时候变得不活跃,那么这可能会有问题.

旁注:在我的服务器(不是示例)中,程序实际上并没有连接到shell,因此无论如何与控制台交互都没有意义.现在它可行,但我正在寻找"正确"的方式,假设有一个.

package main

import (
    "fmt"
    "time"
)

func main() {
    go forever()
    //Keep this goroutine from exiting
    //so that the program doesn't end.
    //This is the focus of my question.
    fmt.Scanln()
}

func forever() {
    for ; ; {
    //An example goroutine that might run
    //indefinitely. In actual implementation
    //it might block on a chanel receive instead
    //of time.Sleep for example.
        fmt.Printf("%v+\n", time.Now())
        time.Sleep(time.Second)
    }
}
Run Code Online (Sandbox Code Playgroud)

pet*_*rSO 55

永远阻止.例如,

package main

import (
    "fmt"
    "time"
)

func main() {
    go forever()
    select {} // block forever
}

func forever() {
    for {
        fmt.Printf("%v+\n", time.Now())
        time.Sleep(time.Second)
    }
}
Run Code Online (Sandbox Code Playgroud)

  • @dark_ruby:它适用于最新版本的 Go:`go version devel +13c44d2 Sat Jun 20 10:35:38 2015 +0000 linux/amd64`。我们如何才能准确地重现您的失败?非常精确,例如,“latest go”太含糊了。 (2认同)

小智 27

Go的运行时的当前设计假定程序员负责检测何时终止goroutine以及何时终止程序.程序员需要计算goroutines以及整个程序的终止条件.程序可以通过调用os.Exit或从main()函数返回以正常方式终止.

main()通过立即接收所述频道来创建频道并延迟退出是防止main退出的有效方法.但它没有解决检测何时终止程序的问题.

如果在main()函数进入wait-for-all-goroutines-to-terminate循环之前无法计算goroutine的数量,则需要发送增量,以便该main函数可以跟踪正在运行的goroutine的数量:

// Receives the change in the number of goroutines
var goroutineDelta = make(chan int)

func main() {
    go forever()

    numGoroutines := 0
    for diff := range goroutineDelta {
        numGoroutines += diff
        if numGoroutines == 0 { os.Exit(0) }
    }
}

// Conceptual code
func forever() {
    for {
        if needToCreateANewGoroutine {
            // Make sure to do this before "go f()", not within f()
            goroutineDelta <- +1

            go f()
        }
    }
}

func f() {
    // When the termination condition for this goroutine is detected, do:
    goroutineDelta <- -1
}
Run Code Online (Sandbox Code Playgroud)

另一种方法是用通道替换通道sync.WaitGroup.这种方法的一个缺点是wg.Add(int)需要在调用之前调用wg.Wait(),因此有必要创建至少1个goroutine in,main()而后续的goroutine可以在程序的任何部分创建:

var wg sync.WaitGroup

func main() {
    // Create at least 1 goroutine
    wg.Add(1)
    go f()

    go forever()
    wg.Wait()
}

// Conceptual code
func forever() {
    for {
        if needToCreateANewGoroutine {
            wg.Add(1)
            go f()
        }
    }
}

func f() {
    // When the termination condition for this goroutine is detected, do:
    wg.Done()
}
Run Code Online (Sandbox Code Playgroud)


jma*_*ney 17

Go的运行时包有一个名为的函数runtime.Goexit,可以完全按照你的意愿执行.

从主goroutine调用Goexit会终止那个没有func main返回的goroutine.由于func main尚未返回,程序继续执行其他goroutine.如果所有其他goroutine退出,程序崩溃.

操场上的例子

package main

import (
    "fmt"
    "runtime"
    "time"
)

func main() {
    go func() {
        time.Sleep(time.Second)
        fmt.Println("Go 1")
    }()
    go func() {
        time.Sleep(time.Second * 2)
        fmt.Println("Go 2")
    }()

    runtime.Goexit()

    fmt.Println("Exit")
}
Run Code Online (Sandbox Code Playgroud)

  • @Nate是正确的.例如,查看[code](https://play.golang.org/p/8N5Yr3ZNPT),Go例程2退出程序,程序不会崩溃.如果Goexit崩溃你的程序,那是因为所有其他Go例程都已完成.我很喜欢它崩溃的方式,让我知道它没有做任何事情,不像说`select {}`即使没有发生任何事情也会永远阻止它. (2认同)

jas*_*siu 10

没有人提到 signal.Notify(c chan<- os.Signal, sig ...os.Signal)

例子:

package main

import (
    "fmt"
    "time"
    "os"
    "os/signal"
    "syscall"
)

func main() {
    go forever()

    quitChannel := make(chan os.Signal, 1)
    signal.Notify(quitChannel, syscall.SIGINT, syscall.SIGTERM)
    <-quitChannel
    //time for cleanup before exit
    fmt.Println("Adios!")
}

func forever() {
    for {
        fmt.Printf("%v+\n", time.Now())
        time.Sleep(time.Second)
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 对于正在运行的服务,这正是我所寻找的! (2认同)
  • 很好的答案!很简单。 (2认同)

Bab*_*aba 9

这是一个永远使用通道的简单块

package main

import (
    "fmt"
    "time"
)

func main() {
    done := make(chan bool)
    go forever()
    <-done // Block forever
}

func forever() {
    for {
        fmt.Printf("%v+\n", time.Now())
        time.Sleep(time.Second)
    }
}
Run Code Online (Sandbox Code Playgroud)