怎么阻止一个Go程序退出?

给大家分享一个阻止Go程序退出的方法集合,其中还是有一些脑洞大开的方法。你有什么别的方法可以阻止Go程序退出么,欢迎在留言区留言。

像下面这样的程序,程序一运行就是退出了,原因在于主goroutine执行完了,子goroutine虽然存在,但是没能阻止主goroutine的执行:

1
2
3
4
5
6
7
package main
import "net/http"
func main() {
go http.ListenAndServe(":8080", nil)
}

我整理了11个方法,看看你是否想得到。

方法一: 死循环

这个方法是一个傻傻的方法,会耗费一个CPU核空转。

1
2
3
4
5
6
7
8
9
10
package main
import "net/http"
func main() {
go http.ListenAndServe(":8080", nil)
for {
}
}

方法二:select{}

这是我写例子的时候常用的一个技巧,使用不带case的select语句,字数够少,所以我喜欢用。

1
2
3
4
5
6
7
8
9
package main
import "net/http"
func main() {
go http.ListenAndServe(":8080", nil)
select {}
}

方法三: 从一个不带缓存的channel中读数据或者放数据

两种方法都会被阻塞。

1
2
3
4
5
6
7
8
9
10
11
package main
import "net/http"
func main() {
go http.ListenAndServe(":8080", nil)
c := make(chan struct{})
// <-c
c <- struct{}{}
}

方法四: 从一个nil channel中读数据或者放数据

两种方法都会被阻塞。

1
2
3
4
5
6
7
8
9
10
11
12
package main
import "net/http"
func main() {
go http.ListenAndServe(":8080", nil)
var c chan struct{}
// <-c
c <- struct{}{}
}

方法五:互斥锁

读写锁也类似。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package main
import (
"net/http"
"sync"
)
func main() {
go http.ListenAndServe(":8080", nil)
var m sync.Mutex
m.Lock()
m.Lock()
}

方法六:WaitGroup

也许你已经掌握套路了,很多同步原语都可以,比如Cond、信号量,我们就不重复介绍了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package main
import (
"net/http"
"sync"
)
func main() {
go http.ListenAndServe(":8080", nil)
var wg sync.WaitGroup
wg.Add(1)
wg.Wait()
}

方法七: 阻塞I/O

最简单的使用os.Stdin,还可以使用文件、socket等,只要阻塞I/O即可。

1
2
3
4
5
6
7
8
9
10
11
12
package main
import (
"net/http"
"os"
)
func main() {
go http.ListenAndServe(":8080", nil)
os.Stdin.Read(make([]byte, 1))
}

方法八: 使用 signal.Notify

这是我在产品中经常使用的一个技巧,捕获os.Signal,实现优雅的退出。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package main
import (
"net/http"
"os"
"os/signal"
)
func main() {
go http.ListenAndServe(":8080", nil)
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt)
<-c
}

方法九: 使用 signal.Notify的变形

使用Context,可以把context传递给子goroutine,Context也适合和select配套使用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package main
import (
"context"
"net/http"
"os"
"os/signal"
)
func main() {
go http.ListenAndServe(":8080", nil)
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt)
defer stop()
<-ctx.Done()
}

方法十: fmt.Scanln()

阻塞I/O更简洁的方式。

1
2
3
4
5
6
7
8
9
10
11
12
package main
import (
"fmt"
"net/http"
)
func main() {
go http.ListenAndServe(":8080", nil)
fmt.Scanln()
}

方法十一: runtime.Goexit()

主程序退出但是函数却不返回,直到子goroutine完成。

1
2
3
4
5
6
7
8
9
10
11
12
package main
import (
"net/http"
"runtime"
)
func main() {
go http.ListenAndServe(":8080", nil)
runtime.Goexit()
}

你还知道哪些方法?