Go 语言能够实现高并发的原因主要归功于其高效的并发模型
其中一点就是go语言特有的goroutine
goroutine也称协程,和进程、线程的关系和区别是什么?
-
进程
进程是操作系统分配资源(如内存、文件句柄等)的最小单位,每个进程通常拥有自己的独立内存空间,进程之间相互独立
因为进程需要大量的系统资源,所以进程的创建和切换开销很大 -
线程
线程是操作系统进行调度的最小单位,属于进程的子任务,一个进程可以有多个线程,它们共享进程的资源(通过共享内存进行通信)。虽然线程的创建和切换比进程轻量,但仍然有较高的系统开销,尤其在多线程上下文切换频繁的情况下 -
协程
协程是Go语言中比线程更轻量级的并发单元。
通过go关键字创建,协程是用户态的线程,操作均由用户态完成,不是交给操作系统的。
一个线程可以承载多个协程,减少了上下文切换的开销。而且协程开销非常小,一个协程只有几KB,大幅度降低了资源的使用。并通过channel来通信,而不是共享内存。
可以将进程比作独立的房子,每个房子有自己独立的资源和环境;线程是房子里的多个房间,房间内可以共享房子内的资源;而协程就像在房间里灵活运行的众多小任务。
Go 语言提倡的并发编程理念是:“不要通过共享内存来通信,而是通过通信来共享内存。”
- 什么是共享内存通信? 什么是通信来共享内存?
-
共享内存通信
在传统的多线程并发编程中,共享内存通常是线程之间交换数据的方式。
多个线程可以同时访问同一块内存区域,读写共享变量。这种要求使用锁、互斥量等来控制对共享数据的访问,但同步控制代码复杂、容易出现死锁、数据竞争等问题。 -
通信共享内存
Go语言中Channel是一种用于协程之间通信的工具。通过Channel传递值,从而确保同步和数据安全性,Channel提供了内置的阻塞和同步机制,不需要显式地管理锁和同步。会比共享内存更加灵活方便
- 快速上手
下面以一个调用10次求和函数为例,计算Add所需的时长
package main
import (
"fmt"
"time"
)
func Add(a, b int) int {
time.Sleep(100) //假设处理时间
return a + b
}
func main() {
start := time.Now()
for i := 1; i < 10; i++ {
Add(i, i)
}
end := time.Now()
fmt.Println(end.Sub(start).Nanoseconds()) // 100140100ns
start = time.Now()
for i := 1; i < 10; i++ {
go func() {
Add(i, i)
}()
}
end = time.Now()
fmt.Println(end.Sub(start).Nanoseconds()) //0ns
}