goroutine

本文探讨了Go语言中的goroutine概念,它是轻量级线程,内存共享,易于创建和管理。此外,文章介绍了Go的CSP并发模型,强调了通过channel进行通信的重要性。还讨论了goroutine的优点如低内存消耗和小切换成本,以及其缺点如非公平调度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

goroutine

goroutine是Go语言提供的一种用户态线程,有时我们也称之为协程。但是它比线程更小,十几个goroutine可能体现在底层就是五六个线程,Go语言内部实现了这些goroutine之间的内存共享。执行goroutine只需极少的栈内存(大概是4~5KB),当然会根据相应的数据伸缩。也正因为如此,可同时运行成千上万个并发任务。goroutine比thread更易用、更高效、更轻便。

线程和协程

  • 进程:拥有自己独立的堆和栈,既不共享堆,亦不共享栈,进程由操作系统调度。
  • 线程:拥有自己独立的栈和共享的堆,共享堆,不共享栈,线程亦由操作系统调度。
  • 协程 :和线程一样共享堆,不共享栈,协程由程序员在协程的代码里显示调度。

创建goroutine

创建goroutine,只需要在函数调用语句前添加go关键字。

但是如果主函数先退出,其他的goroutine也会自动退出。

如果这个函数有返回值,那么这个返回值会被丢弃。

func newTask() {
    fmt.Println("new goroutine")
}

func main() {
    //创建一个 goroutine,启动另外一个任务
    go newTask()

    fmt.Println("main goroutine exit")
}

Go并发模型

Go实现了两种并发形式。

  • 多线程共享内存。其实就是Java或者C++等语言中的多线程开发。
  • CSP(communicating sequential processes)并发模型。这是Go语言特有的,也是Go语言推荐的。

普通的线程并发模型,就是像Java、C++、或者Python,他们线程间通信都是通过共享内存的方式来进行的。非常典型的方式就是,在访问共享数据(例如数组、Map、或者某个结构体或对象)的时候,通过锁来访问。

CSP讲究的是“以通信的方式来共享内存”。

DO NOT COMMUNICATE BY SHARING MEMORY; INSTEAD, SHARE MEMORY BY COMMUNICATING.
“不要以共享内存的方式来通信,相反,要通过通信来共享内存。”

Go的CSP并发模型,是通过goroutinechannel来实现的。

  • goroutine 是Go语言中并发的执行单位。有点抽象,其实就是和传统概念上的”线程“类似,可以理解为”线程“。
  • channel是Go语言中各个并发结构体(goroutine)之前的通信机制。 通俗的讲,就是各个goroutine之间通信的”管道“,有点类似于Linux中的管道。
var ch = make(chan string)

func send() {
	fmt.Println("send data to ch")
	ch <- "test"
}

func recive() {
	result := <-ch
	fmt.Printf("get data from ch: %s", result)
}

func main() {
	go recive()
	go send()
	time.Sleep(5 * time.Second)
}

优点

  • 内存消耗更少:Goroutine所需要的内存通常只有2kb,而线程则需要1Mb
  • 创建与销毁的开销更小:由于线程创建时需要向操作系统申请资源,并且在销毁时将资源归还,因此它的创建和销毁的开销比较大。相比之下,goroutine的创建和销毁是由go语言在运行时自己管理的,因此开销更低。
  • 切换开销更小线程的调度方式是抢占式的,如果一个线程的执行时间超过了分配给它的时间片,就会被其它可执行的线程抢占;而goroutine的调度是协同式的,它不会直接地与操作系统内核打交道。

缺点

  • 协程调度机制无法实现公平调度:因为协程的调度是非入侵式的,系统不会为他分配资源。
### Goroutine 工作机制 在 Go 语言中,Goroutine 是一种轻量级的线程管理机制[^2]。当函数前加上 `go` 关键字时,该函数就会在一个新的 Goroutine 中异步执行。Go 运行时会自动管理和调度这些 Goroutine。 #### 创建和销毁成本低 相比于传统操作系统级别的线程,Goroutine 的创建和销毁代价极小。每个 Goroutine 只需大约 4~5 KB 的栈空间,并且可以根据需要动态调整大小。这种高效的资源利用方式使得可以在单个进程中轻松启动成千上万个 Goroutine 来处理并发任务[^3]。 #### 调度器的作用 Go 运行时内部有一个专门的任务调度器负责分配 CPU 时间给各个活跃的 Goroutine。这个调度器能够根据当前系统的负载情况智能地平衡各 Goroutine 执行时间片,从而保证整体性能最优[^1]。 --- ### 使用方法示例 下面是一个简单的例子展示了如何使用 goroutines 实现基本的并发操作: ```go package main import ( "fmt" "time" ) func say(s string) { for i := 0; i < 5; i++ { time.Sleep(100 * time.Millisecond) fmt.Println(s) } } func main() { go say("world") // 启动一个新的goroutine去执行say函数 say("hello") // 主goroutine继续在这里阻塞直到完成自己的工作 } ``` 在这个例子中,`main()` 函数启动了一个新 goroutine 来打印 "world" 字符串五次的同时自己也打印了 "hello"[^4]。 需要注意的是,在上面的例子中如果希望等待子 goroutine 完成后再退出程序,则可以通过 WaitGroup 或者 Channel 等同步原语来实现这一点。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_李少侠_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值