goroutine

并发设计:将程序分成多个可独立执行的部分的结构化程序的设计方法

并发不是并行。并发是应用结构设计相关的概念,而并行只是程序执行期的概念,并行的必要条件是具有多个处理器或多核处理器,否则无论是否是并发的设计,程序执行时都有且仅有一个任务可以被调度到处理器上执行。

goroutine是由 Go 运行时(runtime)负责调度的、轻量的用户级线程

  • 资源占用小,每个 goroutine 的初始栈大小仅为 2k;
  • 由 Go 运行时而不是操作系统调度,goroutine 上下文切换在用户层完成,开销更小;
  • 在语言层面而不是通过标准库提供。goroutine 由go关键字创建,一退出就会被回收或销毁,开发体验更佳;
  • 语言内置 channel 作为 goroutine 间通信原语,为并发设计提供了强大支撑。

基本用法

基于已有的具名函数 / 方法创建 goroutine

go fmt.Println("I am a goroutine")

基于匿名函数 / 闭包创建 goroutine

var c = make(chan int)
go func(a, b int) {
    c <- a + b
}(3,4)
fmt.Println(c)

创建 goroutine 后,go 关键字不会返回 goroutine id 之类的唯一标识 goroutine 的 id,也不要尝试去得到这样的 id 并依赖它。另外,和线程一样,一个应用内部启动的所有 goroutine 共享进程空间的资源,如果多个 goroutine 访问同一块内存数据,将会存在竞争,需要进行 goroutine 间的同步。

goroutine 的执行函数的返回,就意味着 goroutine 退出。

如果 main goroutine 退出了,那么也意味着整个应用程序的退出

goroutine 执行的函数或方法即便有返回值,Go 也会忽略这些返回值。所以,如果要获取 goroutine 执行后的返回值,需要另行考虑其他方法,比如通过 goroutine 间的通信来实现。

goroutine 间的通信

CSP模型:一个符合 CSP 模型的并发程序应该是一组通过输入输出原语连接起来的 P 的集合

Go引入了 goroutine(P)之间的通信原语channel。goroutine 可以从 channel 获取输入数据,再将处理后得到的结果数据通过 channel 输出。

package main

import (
	"errors"
	"fmt"
	"time"
)

func spawn(f func() error) <-chan error {
	c := make(chan error)
	go func() {
		c <- f()
	}()
	return c
}
func main() {
	c := spawn(func() error {
		time.Sleep(2 * time.Second)
		return errors.New("timeout")
	})
	fmt.Println(<-c)
}

这个示例在 main goroutine 与子 goroutine 之间建立了一个元素类型为 error 的 channel,子 goroutine 退出时,会将它执行的函数的错误返回值写入这个 channel,main goroutine 可以通过读取 channel 的值来获取子 goroutine 的退出状态。

posted @ 2022-03-21 20:24  请务必优秀  阅读(272)  评论(0编辑  收藏  举报