广告
返回顶部
首页 > 资讯 > 后端开发 > GO >Go语言CSP并发模型实现MPG
  • 152
分享到

Go语言CSP并发模型实现MPG

2024-04-02 19:04:59 152人浏览 独家记忆
摘要

目录golang调度机制并发(concurrency)和并行(parallellism)Go的CSP并发模型Go并发模型的实现原理用户级线程模型内核级线程模型两级线程模型Go线程实现

Golang调度机制

最近抽空研究、整理了一下Golang调度机制,学习了其他大牛的文章。把自己的理解写下来。如有错误,请指正!!!

golang的goroutine机制有点像线程池

一、go 内部有三个对象: P对象(processor) 代表上下文(或者可以认为是cpu),M(work thread)代表工作线程,G对象(goroutine).

二、正常情况下一个cpu对象启一个工作线程对象,线程去检查并执行goroutine对象。碰到goroutine对象阻塞的时候,会启动一个新的工作线程,以充分利用cpu资源。所有有时候线程对象会比处理器对象多很多

我们用如下图分别表示P、M、G

在单核情况下,所有goroutine运行在同一个线程(M0)中,每一个线程维护一个上下文(P),任何时刻,一个上下文中只有一个goroutine,其他goroutine在runqueue中等待。一个goroutine运行完自己的时间片后,让出上下文,自己回到runqueue中(如下图左边所示)。

当正在运行的G0阻塞的时候(可以需要io),会再创建一个线程(M1),P转到新的线程中去运行。

当M0返回时,它会尝试从其他线程中“偷”一个上下文过来,如果没有偷到,会把goroutine放到global runqueue中去,然后把自己放入线程缓存中。上下文会定时检查global runqueue。

Go语言是为并发而生的语言,Go语言是为数不多的在语言层面实现并发的语言;也正是Go语言的并发特性,吸引了全球无数的开发者。

并发(concurrency)和并行(parallellism)

并发(concurrency):两个或两个以上的任务在一段时间内被执行。我们不必care这些任务在某一个时间点是否是同时执行,可能同时执行,也可能不是,我们只关心在一段时间内,哪怕是很短的时间(一秒或者两秒)是否执行解决了两个或两个以上任务。

并行(parallellism):两个或两个以上的任务在同一时刻被同时执行。

并发说的是逻辑上的概念,而并行,强调的是物理运行状态。并发“包含”并行。

Go的CSP并发模型

Go实现了两种并发形式。第一种是大家普遍认知的:多线程共享内存。其实就是Java或者c++等语言中的多线程开发。另外一种是Go语言特有的,也是Go语言推荐的:CSP(communicating sequential processes)并发模型。

CSP并发模型是在1970年左右提出的概念,属于比较新的概念,不同于传统的多线程通过共享内存来通信,CSP讲究的是“以通信的方式来共享内存”。

请记住下面这句话:

Do not communicate by sharing memory; instead, share memory by communicating.

“不要以共享内存的方式来通信,相反,要通过通信来共享内存。”

普通的线程并发模型,就是像Java、C++、或者python,他们线程间通信都是通过共享内存的方式来进行的。非常典型的方式就是,在访问共享数据(例如数组、Map、或者某个结构体或对象)的时候,通过来访问,因此,在很多时候,衍生出一种方便操作的数据结构,叫做“线程安全的数据结构”。例如Java提供的包”java.util.concurrent”中的数据结构。Go中也实现了传统的线程并发模型。

Go的CSP并发模型,是通过goroutinechannel来实现的。

goroutine是Go语言中并发的执行单位。有点抽象,其实就是和传统概念上的”线程“类似,可以理解为”线程“。

channel是Go语言中各个并发结构体(goroutine)之前的通信机制。 通俗的讲,就是各个goroutine之间通信的”管道“,有点类似于linux中的管道。

生成一个goroutine的方式非常的简单:Go一下,就生成了。

go f();

通信机制channel也很方便,传数据用channel <- data,取数据用<-channel

在通信过程中,传数据channel <- data和取数据<-channel必然会成对出现,因为这边传,那边取,两个goroutine之间才会实现通信。

而且不管传还是取,必阻塞,直到另外的goroutine传或者取为止。

有两个goroutine,其中一个发起了向channel中发起了传值操作。(goroutine为矩形,channel为箭头)

左边的goroutine开始阻塞,等待有人接收。

这时候,右边的goroutine发起了接收操作。

右边的goroutine也开始阻塞,等待别人传送。

这时候,两边goroutine都发现了对方,于是两个goroutine开始一传,一收。

这便是Golang CSP并发模型最基本的形式。

Go并发模型的实现原理

我们先从线程讲起,无论语言层面何种并发模型,到了操作系统层面,一定是以线程的形态存在的。而操作系统根据资源访问权限的不同,体系架构可分为用户空间和内核空间;内核空间主要操作访问CPU资源、I/O资源、内存资源等硬件资源,为上层应用程序提供最基本的基础资源,用户空间呢就是上层应用程序的固定活动空间,用户空间不可以直接访问资源,必须通过“系统调用”、“库函数”或“shell脚本”来调用内核空间提供的资源。

我们现在的计算机语言,可以狭义的认为是一种“软件”,它们中所谓的“线程”,往往是用户态的线程,和操作系统本身内核态的线程(简称KSE),还是有区别的。

线程模型的实现,可以分为以下几种方式:

用户级线程模型

如图所示,多个用户态的线程对应着一个内核线程,程序线程的创建、终止、切换或者同步等线程工作必须自身来完成。

内核级线程模型

这种模型直接调用操作系统的内核线程,所有线程的创建、终止、切换、同步等操作,都由内核来完成。C++就是这种。

两级线程模型

这种模型是介于用户级线程模型和内核级线程模型之间的一种线程模型。这种模型的实现非常复杂,和内核级线程模型类似,一个进程中可以对应多个内核级线程,但是进程中的线程不和内核线程一一对应;这种线程模型会先创建多个内核级线程,然后用自身的用户级线程去对应创建的多个内核级线程,自身的用户级线程需要本身程序去调度,内核级的线程交给操作系统内核去调度。

Go语言的线程模型就是一种特殊的两级线程模型。暂且叫它“MPG”模型吧。

Go线程实现模型MPG

M指的是Machine,一个M直接关联了一个内核线程。P指的是”processor”,代表了M所需的上下文环境,也是处理用户级代码逻辑的处理器。G指的是Goroutine,其实本质上也是一种轻量级的线程。

三者关系如下图所示:

以上这个图讲的是两个线程(内核线程)的情况。一个M会对应一个内核线程,一个M也会连接一个上下文P,一个上下文P相当于一个“处理器”,一个上下文连接一个或者多个Goroutine。P(Processor)的数量是在启动时被设置为环境变量GOMAXPROCS的值,或者通过运行时调用函数runtime.GOMAXPROCS()进行设置。Processor数量固定意味着任意时刻只有固定数量的线程在运行go代码。Goroutine中就是我们要执行并发的代码。图中P正在执行的Goroutine为蓝色的;处于待执行状态的Goroutine为灰色的,灰色的Goroutine形成了一个队列runqueues

三者关系的宏观的图为:

抛弃P(Processor)

你可能会想,为什么一定需要一个上下文,我们能不能直接除去上下文,让Goroutinerunqueues挂到M上呢?答案是不行,需要上下文的目的,是让我们可以直接放开其他线程,当遇到内核线程阻塞的时候。

一个很简单的例子就是系统调用sysall,一个线程肯定不能同时执行代码和系统调用被阻塞,这个时候,此线程M需要放弃当前的上下文环境P,以便可以让其他的Goroutine被调度执行。

如上图左图所示,M0中的G0执行了syscall,然后就创建了一个M1(也有可能本身就存在,没创建),(转向右图)然后M0丢弃了P,等待syscall的返回值,M1接受了P,将·继续执行Goroutine队列中的其他Goroutine

当系统调用syscall结束后,M0会“偷”一个上下文,如果不成功,M0就把它的Gouroutine G0放到一个全局的runqueue中,然后自己放到线程池或者转入休眠状态。全局runqueue是各个P在运行完自己的本地的Goroutine runqueue后用来拉取新goroutine的地方。P也会周期性的检查这个全局runqueue上的goroutine,否则,全局runqueue上的goroutines可能得不到执行而饿死。

均衡的分配工作

按照以上的说法,上下文P会定期的检查全局的goroutine 队列中的goroutine,以便自己在消费掉自身Goroutine队列的时候有事可做。假如全局goroutine队列中的goroutine也没了呢?就从其他运行的中的P的runqueue里偷。

每个P中的Goroutine不同导致他们运行的效率和时间也不同,在一个有很多P和M的环境中,不能让一个P跑完自身的Goroutine就没事可做了,因为或许其他的P有很长的goroutine队列要跑,得需要均衡。该如何解决呢?

Go的做法倒也直接,从其他P中偷一半!

参考文献:

The Go scheduler

《Go并发编程第一版》

以上就是Go语言CSP并发模型实现MPG的详细内容,更多关于Go CSP并发模型MPG的资料请关注编程网其它相关文章!

您可能感兴趣的文档:

--结束END--

本文标题: Go语言CSP并发模型实现MPG

本文链接: https://www.lsjlt.com/news/149746.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • Go语言CSP并发模型实现MPG
    目录Golang调度机制并发(concurrency)和并行(parallellism)Go的CSP并发模型Go并发模型的实现原理用户级线程模型内核级线程模型两级线程模型Go线程实现...
    99+
    2022-11-13
  • Go语言CSP并发模型怎么实现MPG
    这篇文章主要介绍“Go语言CSP并发模型怎么实现MPG”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“Go语言CSP并发模型怎么实现MPG”文章能帮助大家解决问题。Golang调度机制golang的g...
    99+
    2023-06-30
  • Go语言CSP并发模型goroutine及channel底层实现原理
    目录Go的CSP并发模型(goroutine + channel)1、goroutinegoroutine的优点:2、channel无缓存channel有缓存channel3、Go并...
    99+
    2022-11-13
  • C#代替go采用的CSP并发模型实现
    目录CSP(Communicating sequential processes)在Go中的CSP协程(提升并发的利器)线程线程的开销回归协程协程的目的C#中的协程C#中的CSPGo...
    99+
    2022-11-13
  • 分析Go语言中CSP并发模型与Goroutine的基本使用
    目录一、并发实现模型1.1、多进程1.2、多线程1.3、协程二、共享内存与CSP三、Goroutine一、并发实现模型 1.1、多进程 在之前的文章当中我们曾经介绍过,进程是操作系统...
    99+
    2022-11-12
  • C#怎么实现CSP并发模型
    这篇文章主要介绍“C#怎么实现CSP并发模型”,在日常操作中,相信很多人在C#怎么实现CSP并发模型问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”C#怎么实现CSP并发模型”的疑惑有所帮助!接下来,请跟着小编...
    99+
    2023-06-29
  • golang中的CSP并发模型怎么实现
    本篇内容介绍了“golang中的CSP并发模型怎么实现”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!1. 相关概念: 用户态:当一个进程在执...
    99+
    2023-06-30
  • Go语言的并发模型是如何工作的?
    Go语言是一门开源的编程语言,由Google公司开发。它因其高效的并发模型而广受欢迎。本文将深入探讨Go语言的并发模型是如何工作的,并且给出一些演示代码,帮助读者更好地理解。 一、Go语言中的并发模型 Go语言的并发模型基于Goroutin...
    99+
    2023-07-27
    同步 并发 日志
  • go语言的高级并发模式怎么实现
    Go语言的高级并发模式可以通过以下几种方式实现: 基于通道的并发模式:Go语言通过通道(Channel)来实现并发的通信和同步。...
    99+
    2023-10-27
    go语言
  • Go语言中如何实现并发?
    Go语言作为一门新兴的编程语言,其最大的特点之一就是并发编程,它可以轻松地实现高并发的任务。那么,Go语言中如何实现并发呢?本文将为您详细解答。 goroutine goroutine是Go语言中的轻量级线程,它可以在一个单独的线程中...
    99+
    2023-09-30
    并发 shell bash
  • Go语言如何实现并发爬虫
    这篇文章将为大家详细讲解有关Go语言如何实现并发爬虫,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。1. 单线程爬虫 定义一个用户var Client http.Client主...
    99+
    2023-06-22
  • C语言并发编程模型实例分析
    这篇文章主要介绍“C语言并发编程模型实例分析”,在日常操作中,相信很多人在C语言并发编程模型实例分析问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”C语言并发编程模型实例分析”的疑惑有所帮助!接下来,请跟着小编...
    99+
    2023-06-30
  • Go语言中的并发模型:如何与Shell和Bash对比?
    在计算机科学领域,"并发"一词是指在同一时间内执行多个独立的任务。在现代编程语言中,支持并发的语言越来越普遍。其中,Go语言因其强大的并发模型而备受推崇。本文将讨论Go语言的并发模型,并将其与Shell和Bash进行对比。 一、Shell...
    99+
    2023-09-30
    并发 shell bash
  • Go语言并发爬虫的具体实现
    目录写在前面1. 单线程爬虫2. 多线程爬虫2.1 channel main函数2.2 sync.WaitGroup3. 源码地址写在前面 这篇文章主要让大家明白多线程爬虫,...
    99+
    2022-06-07
    爬虫 GO 并发 go语言
  • go语言如何实现并发网络爬虫
    本篇内容主要讲解“go语言如何实现并发网络爬虫”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“go语言如何实现并发网络爬虫”吧!首先我的思路是看一下爬虫的串行实现,然后通过两个并发实现:一个使用锁...
    99+
    2023-07-05
  • 如何在go语言项目中实现并发
    这期内容当中小编将会给大家带来有关如何在go语言项目中实现并发,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。1、启动go语言的协程package main import (&...
    99+
    2023-06-08
  • 如何在Go语言中实现并发编程?
    Go语言是一种现代化的编程语言,它的并发编程特性使其成为开发高性能网络应用和分布式系统的理想选择。本文将介绍Go语言中的并发编程,包括协程、通道和锁等重要概念,以及如何使用它们来实现高效的并发编程。 协程 协程是Go语言中并发编程的核心概...
    99+
    2023-06-21
    并发 ide npm
  • GO并发模型的实现原理是什么
    这篇文章主要介绍了GO并发模型的实现原理是什么的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇GO并发模型的实现原理是什么文章都会有所收获,下面我们一起来看看吧。前言请记住下面这句话:DO NOT COMMUNI...
    99+
    2023-06-30
  • Go语言的并发模型是否适合处理二维码数据类型?
    Go语言作为一门高效、轻量级的编程语言,在编写并发程序时表现出色,因此在许多领域被广泛使用。但是,对于处理二维码数据类型来说,Go语言的并发模型是否适合呢?在本文中,我们将探讨这个问题,并尝试给出一个答案。 首先,让我们先来了解一下什么是二...
    99+
    2023-09-03
    并发 数据类型 二维码
  • Go语言在Linux上如何实现自然语言处理并发?
    自然语言处理(NLP)是一个涉及语言学、计算机科学和人工智能的领域,它旨在使计算机能够理解人类语言并进行交互。在实现自然语言处理过程中,并发性能是一个重要的问题。在本文中,我们将探讨如何使用Go语言在Linux上实现高效的自然语言处理并发...
    99+
    2023-09-16
    自然语言处理 并发 linux
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作