Go入门指南(协程) | 豆包MarsCodeAI刷题

93 阅读3分钟

Go 语言能够实现高并发的原因主要归功于其高效的并发模型
其中一点就是go语言特有的goroutine

  1. goroutine也称协程,和进程、线程的关系和区别是什么?
  • 进程
    进程是操作系统分配资源(如内存、文件句柄等)的最小单位,每个进程通常拥有自己的独立内存空间,进程之间相互独立
    因为进程需要大量的系统资源,所以进程的创建和切换开销很大

  • 线程
    线程是操作系统进行调度的最小单位,属于进程的子任务,一个进程可以有多个线程,它们共享进程的资源(通过共享内存进行通信)。虽然线程的创建和切换比进程轻量,但仍然有较高的系统开销,尤其在多线程上下文切换频繁的情况下

  • 协程
    协程是Go语言中比线程更轻量级的并发单元。
    通过go关键字创建,协程是用户态的线程,操作均由用户态完成,不是交给操作系统的。
    一个线程可以承载多个协程,减少了上下文切换的开销。而且协程开销非常小,一个协程只有几KB,大幅度降低了资源的使用。并通过channel来通信,而不是共享内存

可以将进程比作独立的房子,每个房子有自己独立的资源和环境;线程是房子里的多个房间,房间内可以共享房子内的资源;而协程就像在房间里灵活运行的众多小任务。

Go 语言提倡的并发编程理念是:“不要通过共享内存来通信,而是通过通信来共享内存。”

  1. 什么是共享内存通信? 什么是通信来共享内存?
  • 共享内存通信
    在传统的多线程并发编程中,共享内存通常是线程之间交换数据的方式。
    多个线程可以同时访问同一块内存区域,读写共享变量。这种要求使用锁、互斥量等来控制对共享数据的访问,但同步控制代码复杂、容易出现死锁、数据竞争等问题。

  • 通信共享内存
    Go语言中 Channel 是一种用于协程之间通信的工具。通过 Channel 传递值,从而确保同步和数据安全性,Channel 提供了内置的阻塞和同步机制,不需要显式地管理锁和同步。会比共享内存更加灵活方便

  1. 快速上手

下面以一个调用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

}