Go并发的方法之goroutine模型与调度策略

目录
  • 单进程操作系统
  • 多线程/多进程操作系统
  • 1:N模型
  • M:N模型
    • goroutine
    • goroutine早期调度器
    • GMP
    • 调度器设计策略
      • 复用线程
      • 并行
      • 抢占
      • 全局队列

学习刘丹冰《8小时转职golang工程师》,本节都是原理

单进程操作系统

早期的单进程操作系统,可以理解为只有一个时间轴,CPU顺序执行每一个进程/线程,这种顺序执行的方式,CPU同一时间智能处理一个指令,一个任务一个任务去处理

这样就会导致进程阻塞的话,CPU就会卡在当前进程,一直在等待,CPU就会浪费

多线程/多进程操作系统

CPU利用轮询机制,调度各个进程,每个进程都分配固定的时间片,这个时间片很小,先执行进程A,如果A结束了,那没问题切换到下一进程B,但如果时间片内A没结束,CPU不管A没结束,会强制切换到下一进程B,以此类推,CPU就避免了阻塞在某一进程

但这样的问题是,在频繁切换进程的过程中,进程的切换就必然会导致切换成本,例如保存当前线程状态,系统调用,环境的上下文切换,各种拷贝复制就会导致时间浪费,大部分时间都用在切换了,进程数量越多,切换的浪费就越大

因此软件的目标就是提高CPU利用率

另一方面,进程的内存占用也是很大的问题

1:N模型

程序员的任务就是在用户态调接口,开发业务,内核态负责调硬件,调系统资源,用户线程和内核线程一一绑定,CPU只需要管内核线程,这样的内核线程称为thread,用户线程称为co-routine,thread通过管理协程调度器,管理多个协程,这样CPU还是只管理一个线程

这样就在用户态实现了并发,CPU也不用切换了,只用在协程切换时消耗很少的资源,解决了CPU高消耗调度的问题

这样的弊端是有一个协程阻塞了,下一个协程就不能执行了

M:N模型

M个线程通过协程调度器,管理多个协程,CPU的调度优化我们没法做,这个协程调度器就非常重要了

goroutine

在go中,协程co-routine被改为goroutine,一个goroutine只占几kb,因此可以有大量的goroutine存在,另一方面goroutine 的调度器非常灵活

goroutine早期调度器

早期的goroutine调度器有一个全局的goroutine队列,这个队列有一个锁,每个线程要创建、销毁、运行一个goroutine都要先获取一个锁,然后执行goroutine,执行完毕后再把锁还回去

这样的问题是激烈的锁竞争

另一方面,当一个线程执行一个goroutine时,这个goroutine新创建了一个goroutine,为了保证并发,这个新goroutine肯定得执行,这个新创建的就被放在下一个线程去执行,这实际上不满足程序的局部性原理

另外系统在频繁调用不同的线程还是会造成系统开销

GMP

在操作系统中,有一个操作系统调度器,用来调度CPU,来处理不同的内核线程,在这之上每个线程有一个处理器,这个处理器里有goroutine的各种资源,堆、栈、数据等,这样的处理器称为P,每个P管理一个自己的本地队列,这个队列里存放着这个P要处理的goroutine,goroutine要被处理时,线程获取一个P,P调度出一个goroutine交给线程,线程对goroutine进行处理,除了P的本地队列外,还有一个全局队列,里面也存放着要运行的goroutine

调度器设计策略

复用线程

work stealing

当一个线程正在执行一个goroutine时,并且它的P本地队列里还有待执行的goroutine,别的P空闲的话就会帮它的线程偷取一个goroutine

↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓

hand off

如果说一个P正执行的goroutine突然阻塞了,比如在等待输入之类的,那么这时候这个P的线程,也就是这个CPU实际上没有被利用,这时候就会创建/唤醒一个新的线程,这时候让阻塞的goroutine继续阻塞,当前CPU变为睡眠状态,但把物理CPU切换走到不阻塞的线程上,其余的P和P的本地队列直接被转移到新的线程上控制,如果之前的阻塞routine又不阻塞了,那把这个goroutine又加入到别的队列后

↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓

并行

P的数量可以宏定义,例如为CPU核心数/2

抢占

在1:1模型中,一个协程和一个线程绑定,当有别的协程需要运行时,当前的协程除非释放出这个线程资源,否则新来的就只能等着

而goroutine的机制是,每个goroutine只有10ms,时间用完后新的goroutine一定会抢占这个CPU,没有谁优先,大家都很平均

全局队列

一个线程如果没有要执行的goroutine,根据work stealing,去别的本地队列偷,如果别的队列也没得偷,就去全局队列里拿,全局队列里别的goroutine前移

到此这篇关于Go并发的方法之goroutine模型与调度策略的文章就介绍到这了,更多相关Go goroutine模型与调度策略内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Go并发编程之goroutine使用正确方法

    目录 1. 对创建的gorouting负载 1.1 不要创建一个你不知道何时退出的 goroutine 1.2 不要帮别人做选择 1.3 不要作为一个旁观者 1.4 不要创建不知道什么时候退出的 goroutine 1.5 不要创建都无法退出的 goroutine 1.6 确保创建出的goroutine工作已经完成 2. 总结 3. 参考 并发(concurrency): 指在同一时刻只能有一条指令执行,但多个进程指令被快速的轮换执行,使得在宏观上具有多个进程同时执行的效果,但在微观上并不是同时

  • 分析Go语言中CSP并发模型与Goroutine的基本使用

    目录 一.并发实现模型 1.1.多进程 1.2.多线程 1.3.协程 二.共享内存与CSP 三.Goroutine 一.并发实现模型 1.1.多进程 在之前的文章当中我们曾经介绍过,进程是操作系统资源分配的最小单元.所以多进程是在操作系统层面的并发模型,因为所有的进程都是有操作系统的内核管理的.所以每个进程之间是独立的,每一个进程都会有自己单独的内存空间以及上下文信息,一个进程挂了不会影响其他进程的运行.这个也是多进程最大的优点,但是它的缺点也很明显. 最大的缺点就是开销很大,创建.销毁进程的开

  • Golang 语言控制并发 Goroutine的方法

    goroutine 是 Go语言中的轻量级线程实现,由 Go 运行时(runtime)管理.Go 程序会智能地将 goroutine 中的任务合理地分配给每个 CPU. 01介绍 Golang 语言的优势之一是天生支持并发,我们在 Golang 语言开发中,通常使用的并发控制方式主要有 Channel,WaitGroup 和 Context,本文我们主要介绍一下 Golang 语言中并发控制的这三种方式怎么使用?关于它们各自的详细介绍在之前的文章已经介绍过,感兴趣的读者朋友们可以按需翻阅. 02

  • golang gin 框架 异步同步 goroutine 并发操作

    goroutine机制可以方便地实现异步处理 package main import ( "log" "time" "github.com/gin-gonic/gin" ) func main() { // 1.创建路由 // 默认使用了2个中间件Logger(), Recovery() r := gin.Default() // 1.异步 r.GET("/long_async", func(c *gin.Context) {

  • Go并发的方法之goroutine模型与调度策略

    目录 单进程操作系统 多线程/多进程操作系统 1:N模型 M:N模型 goroutine goroutine早期调度器 GMP 调度器设计策略 复用线程 并行 抢占 全局队列 学习刘丹冰<8小时转职golang工程师>,本节都是原理 单进程操作系统 早期的单进程操作系统,可以理解为只有一个时间轴,CPU顺序执行每一个进程/线程,这种顺序执行的方式,CPU同一时间智能处理一个指令,一个任务一个任务去处理 这样就会导致进程阻塞的话,CPU就会卡在当前进程,一直在等待,CPU就会浪费 多线程/多进程

  • 并发编程之Java内存模型volatile的内存语义

    1.volatile的特性 理解volatile特性的一个好办法是把对volatile变量的单个读/写,看成是使用同一个锁对单个读/写操作做了同步. 代码示例: package com.lizba.p1; /** * <p> * volatile示例 * </p> * * @Author: Liziba * @Date: 2021/6/9 21:34 */ public class VolatileFeatureExample { /** 使用volatile声明64位的long型

  • 并发编程之Java内存模型顺序一致性

    目录 1.数据竞争和顺序一致性 1.1 Java内存模型规范对数据竞争的定义 1.2 JMM对多线程程序的内存一致性做的保证 2.顺序一致性内存模型 2.1 特性 2.2 举例说明顺序一致性模型 2.3 同步程序的顺序一致性效果 2.4 未同步程序的执行特性 3. 64位long型和double型变量写原子性 3.1 CPU.内存和总线简述 3.2 long和double类型的操作 简介: 顺序一致性内存模型是一个理论参考模型,处理器的内存模型和编程语言的内存模型都会以顺序一致性内存模型作为参照

  • 并发编程之Java内存模型锁的内存语义

    目录 1.锁的释放-获取建立的happens-before关系 2.锁释放和获取的内存语义 3.锁内存的语义实现 4.concurrent包的实现 简介: 锁的作用是让临界区互斥执行.本文阐述所得另一个重要知识点--锁的内存语义. 1.锁的释放-获取建立的happens-before关系 锁是Java并发编程中最重要的同步机制.锁除了让临界区互斥执行外,还可以让释放锁的线程向获取同一个锁的线程发送消息. 锁释放-获取的示例代码: package com.lizba.p1; /** * <p>

  • Tomcat并发优化方法介绍

    Tomcat 常用运行模式有3种,分别为 bio,nio,apr.生产环境建议用apr,详细请看上一篇博文<Tomcat之--运行模式> 安装APR [root@liuyazhuang ~]# yum -y install apr apr-devel openssl-devel [root@liuyazhuang ~]# tar zxvf tomcat-native.tar.gz [root@liuyazhuang ~]# cd tomcat-native-1.1.24-src/jni/nat

  • php多线程并发实现方法

    本文实例讲述了php多线程并发实现方法.分享给大家供大家参考,具体如下: java里多线程就是一个new thread的事情,php依赖apache靠着linux底层有一个多线程的办法. 这里就来说说如果你无法操控apache的服务器,如何模拟php并发 <?php if(function_exists('date_default_timezone_set')) { date_default_timezone_set('PRC'); } function a() { $time = time()

  • python 多线程对post请求服务器测试并发的方法

    如下所示: # -*- coding: utf-8 -*- import requests import threading import time class postrequests(): def __init__(self): self.url = '请求网址' self.files = { 'unknown_image':open('刘诗诗.jpg','rb') } def post(self): try: r = requests.post(self.url,files=self.fi

  • PHP curl批处理及多请求并发实现方法分析

    本文实例讲述了PHP curl批处理及多请求并发实现方法.分享给大家供大家参考,具体如下: 在面试过程中遇到一个问题,加入一个一个网站访问一次需要两秒,我们如何实现在2秒左右请求三次? 面试官想问的就是如何使用curl并发处理请求 关于curl_multi_init() 一般来说,想到要用curl_multi_init()时,目的是要同时请求多个url,而不是一个一个依次请求,否则就要curl_init()了. 不过,在使用curl_multi的时候,你可能遇到cpu消耗过高.网页假死等现象,可

  • MySQL 加锁控制并发的方法

    前言 锁总体可以分为乐观锁和悲观锁,简单说,乐观锁用版本号控制,悲观锁用锁控制. 下面是待会要用来测试的数据 # 添加一个user表 CREATE TABLE `users` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'ID', `name` varchar(255) NOT NULL COMMENT '姓名', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSE

  • axios解决高并发的方法:axios.all()与axios.spread()的操作

    前言: 很多时候,我们可能需要同时调用多个后台接口,就会高并发的问题,一般解决这个问题方法: axios.all和axios.spread ***注意这里的$get是封装的axios方法 //方法一: searchTopic() { return this.$axios({ url:'地址1', method:'方式',//get/post/patch/put/deleted params:{//参数get所以用params.post.put用data } }) } //方法二: searchs

随机推荐