Go语言学习笔记—golang并发编程之goroutine

视频来源于B站Go语言基础进阶视频av56860636

文章为自己整理的学习笔记,侵权即删,谢谢支持!


一 goroutine基本介绍

1.1 看一个需求

需求:要求统计 1-9000000000 的数字中,哪些是素数?

分析思路:

  1. 传统的方法,就是使用一个循环,循环的判断各个数是不是素数。很慢
  2. 使用并发或者并行的方式,将统计素数的任务分配给多个 goroutine 去完成,这时就会使用到goroutine。速度提高 4 倍

1.2 进程、线程和协程

  1. 进程就是程序在操作系统中的一次执行过程,是系统进行资源分配调度基本单位。可以简单理解为“正在执行的程序”,进程的局限是创建、撤销和切换的开销比较大。

  2. 线程是进程的一个执行实例,是程序执行的最小单位,它是比进程更小的能独立运行的基本单位

  3. 一个进程可以创建和销毁多个线程,同一个进程中的多个线程可以并发执行

  4. 一个程序至少有一个进程,一个进程至少有一个线程

  5. 进程和线程的关系示意图:

    在这里插入图片描述

  6. 协程是一种用户态的轻量级线程,又称微线程,英文名Coroutine,协程的调度完全由用户控制。与传统的系统级线程和进程相比,协程的最大优势在于其"轻量级",可以轻松创建上百万个而不会导致系统资源衰竭,而线程和进程通常最多也不能超过1万的。这也是协程也叫轻量级线程的原因。

  7. 协程与多线程相比,其优势体现在:协程的执行效率极高。因为子程序切换不是线程切换,而是由程序自身控制,因此,没有线程切换的开销,和多线程比,线程数量越多,协程的性能优势就越明显。Go语言对于并发的实现是靠协程,Goroutine

1.3 并行和并发

  1. 多线程程序在单核上运行,就是并发

  2. 多线程程序在多核上运行,就是并行

  3. 关系示意图:

    在这里插入图片描述

说明:

  1. 并发:因为是在一个cpu上,比如有10个线程,每个线程执行10毫秒(进行轮询操作),从人的角度看,好像这10哥线程都在运行,但从微观上看,在某一个时间点看,其实只有一个线程在执行,这就是并发。
  2. 并行:因为实在多个cpu上(比如有10个cpu),比如有10个线程,每个线程执行10毫秒(各自在不同的cpu上执行),从人的角度看,这10个线程都在运行,并且从微观角度看,在某一时间点看也同时10个线程,这就是并行。

1.4 Go协程和Go主线程

Go语言天然支持高并发是其一个很大的优势。Go 语言内置了 goroutine 机制,使用goroutine可以快速地开发并发程序, 更好的利用多核处理器资源。

Go 主线程(有程序员直接称为线程/也可以理解成进程): 一个 Go 线程上,可以起多个协程,你可以这样理解,协程是轻量级的线程[编译器做优化]。

Go 协程的特点:

  1. 有独立的栈空间
  2. 共享程序堆空间
  3. 调度由用户控制
  4. 协程是轻量级的线程

Go协程和Go主线程关系示意图:

在这里插入图片描述

二 goroutine快速入门

2.1 案例说明

请编写一个程序,完成如下功能:

  1. 在主线程(可以理解成进程)中,开启一个 goroutine, 该协程每隔 1 秒输出 “hello,world”
  2. 在主线程中也每隔一秒输出"hello,golang", 输出 10 次后,退出程序
  3. 要求主线程和 goroutine 同时执行.

2.2 代码实现

package main

import (
	"fmt"
	"strconv"
	"time"
)

func test() {
   
	for i := 1; i <= 10; i++ {
   
		fmt.Println("test协程 hello,world" + strconv.Itoa(i))
		time.Sleep(time.Second)
	}
}

func main() {
    //主线程
	go test() // 开启一个协程

	for i := 1; i <= 10; i++ {
   
		fmt.Println("main主线程 hello,golang" + strconv.Itoa(i))
		time.Sleep(time.Second)
	}
}

运行结果:

[Running] go run "c:\Users\Mechrevo\Desktop\go_pro\test.go"
main主线程 hello,golang1
test协程 hello,world1
test协程 hello,world2
main主线程 hello,golang2
main主线程 hello,golang3
test协程 hello,world3
test协程 hello,world4
main主线程 hello,golang4
main主线程 hello,golang5
test协程 hello,world5
test协程 hello,world6
main主线程 hello,golang6
main主线程 hello,golang7
test协程 hello,world7
test协程 hello,world8
main主线程 hello,golang8
main主线程 hello,golang9
test协程 hello,world9
test协程 hello,world10
main主线程 hello,golang10

[Done] exited with code=0 in 11.131 seconds

2.3 流程说明

在这里插入图片描述

  1. 主线程是一个物理线程,直接作用在 cpu 上的。是重量级的,非常耗费 cpu 资源。
  2. 协程从主线程开启的,是轻量级的线程,是逻辑态。对资源消耗相对小。
  3. Golang 的协程机制是重要的特点,可以轻松的开启上万个协程。其它编程语言的并发机制是一般基于线程的,开启过多的线程,资源耗费大,这里就突显Golang 在并发上的优势了

三 Go并发调度MPG模型

在操作系统提供的内核线程之上,Go搭建了一个特有的两级线程模型。goroutine机制实现了M : N的线程模型,goroutine机制是协程(coroutine)的一种实现,golang内置的调度器,可以让多核CPU中每个CPU执行一个协程。

创建一个协程非常简单,就是在一个任务函数前面添加一个go关键字:

// 用go关键字加上一个函数(这里用了匿名函数)
// 调用就做到了在一个新的“线程”并发执行任务
go func() {
    
    // do something in one new goroutine
}()

3.1 MPG模式基本介绍

Go语言中支撑整个scheduler实现的主要有4个重要结构,分别是M、G、P、Sched, 前三个定义在runtime.h中,Sched定义在proc.c中。

  • Sched结构就是调度器,它维护有存储M和G的队列以及调度器的一些状态信息等。
  • M结构是Machine,系统线程,它由操作系统管理的,goroutine就是跑在M之上的;M是一个很大的结构,里面维护小对象内存cache(mcache)、当前执行的goroutine、随机数发生器等等非常多的信息。
  • P结构是Processor,处理器,它的主要用途就是用来执行goroutine的,它维护了一个goroutine队列,即runqueue。Processor是让我们从N:1调度到M:N调度的重要部分。
  • G是goroutine实现的核心结构,它包含了栈,指令指针,以及其他对调度goroutine很重要的信息,例如其阻塞的channel。

我们分别用三角形,矩形和圆形表示Machine Processor和Goroutine。

在这里插入图片描述

在单核处理器的场景下,所有goroutine运行在同一个M系统线程中,每一个M系统线程维护一个Processor,任何时刻,一个Processor中只有一个goroutine,其他goroutine在runqueue中等待。一个goroutine运行完自己的时间片后,让出上下文,回到runqueue中。 多核处理器的场景下,为了运行goroutines,每个M系统线程会持有一个Processor。

3.2 MPG模式运行的状态1

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值