这是我参与「第五届青训营 」伴学笔记创作活动的第 16 天
什么是协程(和线程对比)
协程(Coroutine)是一种轻量级的线程,它可以在同一线程内运行,而不需要切换线程的上下文。与线程相比,协程具有更小的内存占用和更高的并发能力。在Golang中,协程是一种基本的并发单元,可以通过goroutine关键字来创建。
线程是操作系统分配的一种执行单元,每个线程都有自己的堆栈和程序计数器。在多线程应用程序中,每个线程都可以执行不同的任务,但同时需要共享相同的进程内存空间。线程的切换开销较大,并且线程之间的同步需要使用锁等机制。
相比之下,协程切换的开销非常小,因为它们在同一线程内运行。协程之间的同步可以使用通道等Golang提供的原语来实现,更为简单。
协程和线程各自的优劣
协程的优点:
- 协程的切换开销很小,可以在同一线程内运行,因此协程的并发能力更高。
- 协程不需要像线程那样使用锁等机制进行同步,因此代码更简洁。
- 协程的内存占用比线程更小。
线程的优点:
- 线程是由操作系统分配的,可以在不同的CPU核心上执行,并且可以使用操作系统提供的多线程库来进行同步和通信。
- 线程可以利用多核CPU的优势,因此在某些情况下可以比协程更快。
Go使用协程的例子
下面是一个使用协程并发执行任务的例子:
package main
import (
"fmt"
"sync"
"time"
)
func worker(id int, wg *sync.WaitGroup, jobs <-chan int, results chan<- int) {
defer wg.Done()
for j := range jobs {
fmt.Printf("Worker %d started job %d\n", id, j)
time.Sleep(time.Second)
fmt.Printf("Worker %d finished job %d\n", id, j)
results <- j * 2
}
}
func main() {
numJobs := 10
jobs := make(chan int, numJobs)
results := make(chan int, numJobs)
var wg sync.WaitGroup
// 创建3个协程
for i := 1; i <= 3; i++ {
wg.Add(1)
go worker(i, &wg, jobs, results)
}
// 添加10个任务到任务队列中
for j := 1; j <= numJobs; j++ {
jobs <- j
}
close(jobs)
// 等待所有协程完成任务
wg.Wait()
// 获取结果
for a := 1; a <= numJobs; a++ {
<-results
}
}
例子中使用一个sync.WaitGroup来同步协程的执行。每个协程执行完成后,都会调用wg.Done()来通知WaitGroup,主函数使用wg.Wait()来等待所有协程都执行完成后结束。此外,我们还使用了defer语句来确保在协程退出前调用wg.Done()。
在这个例子中,我们还保留了原来的协程数量和任务分配策略。如果需要改变协程数量或任务分配策略,只需要调整i的取值和任务添加的逻辑即可。