Go语言Goroutinue和管道效率详解

目录
  • goroutinue基本介绍
    • 进程和线程说明
    • 并发和并行
    • 同步和异步
  • Go协程和Go主线程
    • go协程特点
  • goroutinue基本使用
    • 实验代码
    • 效果图
    • 执行流程图
  • goroutinue的调度模型
    • MPG
    • MPG运行状态1
    • MPG运行状态2
  • 管道(channel)
    • 不同协程之间如何通讯
    • 全局变量加锁同步缺陷
    • 管道基本介绍
    • 管道基本使用 声明和定义
    • 管道关闭和遍历
      • 关闭
      • 遍历
    • 管道注意事项
  • 综合案例

goroutinue基本介绍

进程和线程说明

  • 进程介绍程序在操作系统中的一次执行过程,是系统进行资源分配和调度的基本单位
  • 线程只是进程的一个执行实例或流程,是程序执行的最小单元
  • 一个进程可以有多个线程,但是一个线程只能对应一个进程
  • 同一个进程中的多个线程可以并发执行
  • 程序:运行起来的应用程序就称为进程,也就是当程序不运行的时候我们称为程序,当程序运行起来他就是一个进程,通俗的理解就是不运行的时候是程序,运行起来就是进程。程序只有一个,但是进程有多个

并发和并行

  • 并发:多个任务依次执行,执行过程中多个任务可以替换执行,在某一个时刻是一个任务在执行,但是在某个时间段内是多个任务在执行。
  • 并行:多个任务没有顺序,同时执行,最终的执行结果跟耗时最长的任务有关
  • 串行:多个任务依次执行,上一个任务没有完成时不能执行后续的任务,最明显的同步执行过程

同步和异步

  • 同步:描述的就是串行执行过程,多个任务按照顺序依次执行的过程
  • 异步:描述的就是并发和并行的过程,就是多个任务在一个时间段内同时执行,每个任务都不会等待其他任务执行完成后执行

Go协程和Go主线程

Go主线程:一个Go线程上,可以起多个协程,协程是轻量级的线程

go协程特点

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

goroutinue基本使用

实验代码

package main
import (
	"fmt"
	"runtime"
	"strconv"
	"time"
)
func main() {
	//编写一个函数,每隔1s输出"hello,world"
	//要求主线程和gorutine同时执行
	go test()
	//在主线程中,开启一个goroutine,该协程每隔1s输出"hello,world"
	for i:=1;i<=10 ; i++ {
		fmt.Println("main() hello world", strconv.Itoa(i))
		time.Sleep(time.Second)
	}
	//查询Golang运行的cpu数
	fmt.Println(runtime.NumCPU()) //4
	//设置Golang运行的cpu数
	//runtime.GOMAXPROCS(runtime.NumCPU()-1)	//3
}
func test(){
	for i:=1;i<=10 ; i++ {
		fmt.Println("test() hello world",strconv.Itoa(i))
		time.Sleep(time.Second)
	}
}

效果图

执行流程图

goroutinue的调度模型

MPG

MPG运行状态1

MPG运行状态2

管道(channel)

不同协程之间如何通讯

  • 全局变量加锁同步
  • channel

使用全局变量加锁同步改进程序

  • 因为没有对全局变量加锁,因此会出现资源夺取问题,代码会出现错误,提示concurrent map writes
  • 加入互斥锁

全局变量加锁同步缺陷

  • 主线程在等待所有goroutine全部完成的时间很难确定
  • 如果主线程休眠时间长了,会加长等待时间,如果等待时间短了,可能还有goroutine处于工作状态,这时也会随着主线程的结束而结束
  • 不利于多个协程对全局变量的读写操作

管道基本介绍

  • 管道本质介绍一个数据结构-队列
  • 数据是先进先出
  • 线程安全,无需加锁
  • 管道有类型

管道基本使用 声明和定义

管道关闭和遍历

关闭

使用内置函数close可以关闭channel,关闭后,就不能写入数据,但可读

遍历

  • 在使用for--range遍历时,如果channel没有关闭,则回出现deadlock错误
  • 在使用for--range遍历时,如果channel已经关闭,则会正常遍历数据

代码

package main
import "fmt"
func main() {
	//定义管道
	var intChan chan int
	intChan =make(chan int,3)
	//写入数据
	intChan<-10
	intChan<-20
	intChan<-30
	//遍历
	close(intChan) //关闭管道
	for value := range intChan {
		fmt.Printf("%d\t",value) //10	20	30
	}
}

管道注意事项

-`channel可以声明为只读,或者只写性质

  • 使用select可以解决从管道取数据的阻塞问题
  • goroutine中使用recover,解决协程中出现panic,导致程序崩溃问题

综合案例

package main
import "fmt"
func main() {
	numChan := make(chan int, 2000)
	resChan := make(chan int, 2000)
	exitChan := make(chan bool, 8)
	go putNum(numChan) //存放数据
	//开启八个协程
	for i := 0; i < 8; i++ {
		go add(numChan, resChan, exitChan)
	}
	go func() {
		for i:=0;i<8 ;i++  {
			<-exitChan
		}
		close(resChan)
	}()
	for i := 1; i <=2000 ; i++ {
		fmt.Printf("resChan[%d]=%d\n", i, <-resChan)
	}
}
func putNum(numChan chan int) {
	for i := 1; i <= 2000; i++ {
		numChan <- i
	}
	close(numChan)
}
func add(numChan chan int, resChan chan int, exitChan chan bool) {
	for {
		n,ok := <-numChan
		if !ok{
			break
		}
		res := 0
		for i := 1; i <= n; i++ {
			res += i
		}
		resChan <- res
	}
	exitChan<-true
}

以上就是Go语言Goroutinue和管道效率详解的详细内容,更多关于Go Goroutinue 管道效率的资料请关注我们其它相关文章!

(0)

相关推荐

  • goFrame的队列gqueue对比channel使用详解

    目录 channel gqueue 概念 使用场景: 代码演示 打印结果 优势 底层实现 阻止进程销毁 运行结果 总结 channel 首先明确一下channel的作用:用于go协程间的通信. go语言最大的特点就是支持高并发:goroutine和channel是支持高并发的重要组成部分. 单纯地将函数并发执行是没有意义的.函数与函数间需要交换数据才能体现并发执行函数的意义. 如果说 goroutine 是Go程序并发的执行体,channel就是它们之间的连接.channel是可以让一个 gor

  • Go底层channel实现原理及示例详解

    目录 概念: 使用场景: 底层数据结构: 操作: 创建 发送 接收 关闭 案例分析: 概念: Go中的channel 是一个队列,遵循先进先出的原则,负责协程之间的通信(Go 语言提倡不要通过共享内存来通信,而要通过通信来实现内存共享,CSP(Communicating Sequential Process)并发模型,就是通过 goroutine 和 channel 来实现的) 使用场景: 停止信号监听 定时任务 生产方和消费方解耦 控制并发数 底层数据结构: 通过var声明或者make函数创建

  • Go通道channel通过通信共享内存

    目录 引言 通道的声明与创建 接收 & 发送数据 引言 不要通过共享内存来通信 应该通过通信来共享内存 这句话有网友的解释如下: 这句俏皮话具体说来就是,不同的线程不共享内存不用锁,线程之间通讯用通道(channel)同步也用channel. chanel是协程之间传递信息的媒介,优雅地解决了某些后端开发常用语言中随处可见的lock,unlock,临界区等,把从很多线程层面解决的问题移到协程,从而静态地保证没有数据竞争. 通道的声明与创建 伪代码如下: //声明类型 var 通道名 chan 数

  • Golang channel为什么不会阻塞的原因详解

    正文 最近在学通道channel,发现一个简单的demo: package main import "fmt" func main() { chanInt := make(chan int) go func() { chanInt <- 100 }() res := <-chanInt fmt.Println(res) } 输出结果是100,这个没有问题.但是之前在学goroutine的时候有看到过一个例子: package main import "fmt&qu

  • Go语言CSP并发模型goroutine channel底层实现原理

    目录 Go的CSP并发模型(goroutine + channel) 1.goroutine goroutine的优点: 2.channel 无缓存channel 有缓存channel 3.Go并发模型的底层实现原理 4.一个CSP例子 参考Go的CSP并发模型实现:M, P, G Go语言是为并发而生的语言,Go语言是为数不多的在语言层面实现并发的语言. 并发(concurrency):多个任务在同一段时间内运行. 并行(parallellism):多个任务在同一时刻运行. Go的CSP并发模

  • GO语言协程创建使用并通过channel解决资源竞争

    目录 创建协程 主协程终止,子协程也终止 runtime包 Gosched让出CPU时间片 Goexit立即结束当前协程 GOMAXPROCS设置并行CPU核数最大值,并返回之前的值 runtime.NumGoroutine()获取当前运行中的goroutine数量 多任务资源竞争问题 通过channel解决资源竞争问题 主协程如何等其余协程完再退出 创建协程 goroutine是go的设计核心,就是协程主协程终止了,子协程也终止 package main import ( "fmt"

  • Go语言Goroutinue和管道效率详解

    目录 goroutinue基本介绍 进程和线程说明 并发和并行 同步和异步 Go协程和Go主线程 go协程特点 goroutinue基本使用 实验代码 效果图 执行流程图 goroutinue的调度模型 MPG MPG运行状态1 MPG运行状态2 管道(channel) 不同协程之间如何通讯 全局变量加锁同步缺陷 管道基本介绍 管道基本使用 声明和定义 管道关闭和遍历 关闭 遍历 管道注意事项 综合案例 goroutinue基本介绍 进程和线程说明 进程介绍程序在操作系统中的一次执行过程,是系统

  • C语言 function recursion函数递归详解

    目录 function recursion(函数递归) 递归的中心思想为: 程序一 递归的两个必要条件 程序一: 程序二: 练习 求n的阶乘 再来道例题 function recursion(函数递归) 函数递归: 是在 一个 过程 或 函数 在其定义或说明中有 直接 或 间接 调用自身 的一种方法 通常把一个 大型复杂的问题 层层 传化 为一个与 原理相似的 ,规模较小 的问题 递归策略 只需 少量的程序 就可以描述出 解题过程 所需的 多次 重复 计算,大大减少了程序的代码量 递归的中心思想

  • C语言 structural body结构体详解用法

    目录 结构体 结构体类型的声明 举个现实例子 程序实例 结构体成员的类型: 结构体变量的定义和初始化 程序一 结构体嵌套情况下,初始化和定义 结构体成员的访问 结构体传参 程序一: 程序二 结构体 结构是一些值的集合,这些值称为成员变量,结构的每个成员可以是不同类型的变量 结构体类型的声明 创建 结构体类型 没有占 内存空间,因为还 没有 创建变量 举个现实例子                 盖房子 图纸 --------------------> 房子 结构体类型        结构体变量

  • C语言数据结构之单向链表详解分析

    链表的概念:链表是一种动态存储分布的数据结构,由若干个同一结构类型的结点依次串连而成. 链表分为单向链表和双向链表. 链表变量一般用指针head表示,用来存放链表首结点的地址. 每个结点由数据部分和下一个结点的地址部分组成,即每个结点都指向下一个结点.最后一个结点称为表尾,其下一个结点的地址部分的值为NULL(表示为空地址). 特别注意:链表中的各个结点在内存中是可以不连续存放的,具体存放位置由系统分配. 例如:int *ptr ; 因此不可以用ptr++的方式来寻找下一个结点. 使用链表的优点

  • C语言程序环境和预处理详解分析

    目录 一.程序的翻译环境和运行环境 程序的翻译环境 链接阶段 执行环境(运行环境) 二.预处理详解 预定义符号 #define定义标识符 #define定义宏 #define 替换规则 #和##两个预处理的工具 带副作用的宏参数 宏和函数对比 #undef移除宏 命令行定义 条件编译 头文件包含 嵌套文件包含 总结 一.程序的翻译环境和运行环境 重点:任何ANSI C(标准C的程序)的一种实现,存在两个不同的环境 第1种是翻译环境,在这个环境中源代码被转换为可执行的机器指令. 第2种是执行环境,

  • C语言 操作符#与##使用方法详解

    目录 一.# 运算符 二.## 运算符 三.小结 一.# 运算符 # 运算符用于在预处理期将宏参数转换为字符串 # 的转换作用是在预处理期完成的,因此只在宏定义中有效 编译器不知道 # 的转换作用 用法: #define STRING(x) #x printf("%s\n",STRING(Hello World!)); 下面通过一个示例感受一下: test.c: #include <stdio.h> #define STRING(x) #x int main() { pri

  • C语言实现队列的示例详解

    目录 前言 一. 什么是队列 二. 使用什么来实现栈 三. 队列的实现 3.1头文件 3.2 函数的实现 四.完整代码 前言 前一段时间,我们试着用C语言实现了数据结构中的顺序表,单链表,双向循环链表,栈.今天我们再用C语言来实现另一种特殊的线性结构:队列 一. 什么是队列 队列是一种特殊的线性表,特殊之处在于它只允许在表的前端(head)进行删除操作,而在表的后端(tail)进行插入操作,和栈一样,队列是一种操作受限制的线性表.进行插入操作的端称为队尾,进行删除操作的端称为队头. 这个队列就可

  • C语言实现栈的示例详解

    目录 前言 一. 什么是栈 二. 使用什么来实现栈 三. 栈的实现 3.1 头文件 3.2 函数实现 3.3 完整代码 四. 栈的用处 前言 前一段时间,我们试着用C语言实现了数据结构中的顺序表,单链表,双向循环链表.今天我们再用C语言来实现另一种特殊的线性结构:栈 一. 什么是栈 栈(stack)又名堆栈,它是一种运算受限的线性表.限定仅在表尾进行插入和删除操作的线性表.这一端被称为栈顶,相对地,把另一端称为栈底.向一个栈插入新元素又称作进栈.入栈或压栈,它是把新元素放到栈顶元素的上面,使之成

  • Go语言数据结构之插入排序示例详解

    目录 插入排序 动画演示 Go 代码实现 总结 插入排序 插入排序,英文名(insertion sort)是一种简单且有效的比较排序算法. 思想: 在每次迭代过程中算法随机地从输入序列中移除一个元素,并将改元素插入待排序序列的正确位置.重复该过程,直到所有输入元素都被选择一次,排序结束. 插入排序有点像小时候我们抓扑克牌的方式,如果抓起一张牌,我们放在手里:抓起第二张的时候,会跟手里的第一张牌进行比较,比手里的第一张牌小放在左边,否则,放在右边. 因此,对所有的牌重复这样的操作,所以每一次都是插

  • C语言学习之柔性数组详解

    目录 一.前言 二.柔性数组的用法 三.柔性数组的内存分布 四.柔性数组的优势 五.总结 一.前言 仔细观察下面的代码,有没有看出哪里不对劲? struct S { int i; double d; char c; int arr[]; }; 还有另外一种写法: struct S { int i; double d; char c; int arr[0]; }; 你应该一眼就看到了,结构体的最后一个成员数组的写法是int arr[];或者是int arr[0],这两种写法是等价的,意思是这个数组

随机推荐