返回介绍

上卷 程序设计

中卷 标准库

下卷 运行时

源码剖析

附录

9.1.1 结束

发布于 2024-10-12 19:15:49 字数 2732 浏览 0 评论 0 收藏 0

所有用户代码都以 goroutine 执行,包括 main.main 入口函数。
进程结束,不会等待其他正在执行或尚未执行的任务。

func main() {
	go func() {
		defer println("g done.")
		time.Sleep(time.Second)
	}()

	defer println("main done.")
}

// main done.

等待

等待任务结束,可以做得比 time.Sleep 更优雅一些。

  • channel : 信号通知。
  • WaitGroup :等待多个任务结束。
  • Context :上下文通知。
  • Mutex :锁阻塞。

如果只是一次性通知行为,可使用空结构。只要关闭通道,等待(阻塞)即可解除。

func main() {
	q := make(chan struct{})

	go func() {
		defer close(q)
        println("done.")
	}()

	<- q
}

添加计数( WaitGroup.Add )应在创建任务和等待之前,否则可能导致等待提前解除。
可以有多处等待,实现群体性通知。

func main() {
	var wg sync.WaitGroup
    wg.Add(10)
    
	for i := 0; i < 10; i++ {
		go func(id int) {
			defer wg.Done()
			println(id, "done.")
		}(i)
	}
    
	wg.Wait()
}
func main() {
	var wg sync.WaitGroup
    
	for i := 0; i < 10; i++ {
	    
        // 可写在此处,应对未知循环数。
        
        // 但不可放在下面 goroutine 函数内,
        // 因为它可能未执行,下面 Wait 先结束了。

        wg.Add(1)
        
		go func(id int) {
			defer wg.Done()
			println(id, "done.")
		}(i)
	}
    
	wg.Wait()
}

上下文的实现和通道基本一致。

func main() {
	ctx, cancel := context.WithCancel(context.Background())

	go func() {
		defer cancel()
		println("done.")
	}()

	<- ctx.Done()
}

利用锁实现 “同步”,在其他语言很常见,但 Go 更倾向于以通信代替。

func main() {
	var lock sync.Mutex

	lock.Lock()
	go func() {
		defer lock.Unlock()
		println("done.")
	}()

	lock.Lock()
	lock.Unlock()

	println("exit")
}

终止

主动结束任务,有以下几种方式。

  • 调用 runtime.Goexit 终止任务。
  • 调用 os.Exit 结束进程。

在任务调用堆栈(call stack)的任何位置调用 runtime.Goexit 都能立即终止任务。
结束前,延迟调用(defer)被执行。其他任务不受影响。

func main() {
	q := make(chan struct{})

	go func() {
		defer close(q)
		defer println("done.")
        
        // 如果将 b 里面的 Goexit 换成 return,
        // 那只是结束了 b(),而非整个调用堆栈。

		a()
		b()
		c()
	}()

	<- q
}

func a() { println("a") }
func b() { println("b"); runtime.Goexit() }
func c() { println("c") }

// a
// b
// done.

main goroutine 里调用 Goexit ,它会等待其他任务结束,然后崩溃进程。

func main() {
	q := make(chan struct{})

	go func() {
		defer close(q)
		defer println("done.")
		time.Sleep(time.Second)
	}()

	runtime.Goexit()

	<- q
}

// done.
// fatal error: no goroutines (main called runtime.Goexit) - deadlock!

os.Exit 可在任意位置结束进程。不等待其他任务,也不执行延迟调用。

func main() {
	go func() {
		defer println("g done.")
		time.Sleep(time.Second)
	}()

	defer println("main done.")

	os.Exit(-1)
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文