iis服务器助手广告广告
返回顶部
首页 > 资讯 > 后端开发 > GO >Golang中goroutine和channel使用介绍深入分析
  • 149
分享到

Golang中goroutine和channel使用介绍深入分析

Go goroutine与channelGo goroutineGo channel 2023-01-13 15:01:12 149人浏览 独家记忆
摘要

目录1.Goroutine-看一个需求2.进程和线程介绍3.并发和并行4.Go协程和Go主线程5.设置golang运行的CPU数6.channel(管道)看需求1.goroutine

1.goroutine-看一个需求

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

分析:

  • 传统方法,就是使用一个循环,循环的判断各个数是不是素数。
  • 使用并发或并行的方式,将统计素数的任务分配给多个goroutine去完成,这时就会使用到goroutine。

2.进程和线程介绍

  • 进程就是程序在操作系统中的一次执行过程,是系统进行资源分配和调度的基本单位
  • 线程是进程的一个执行实例,是程序执行的最小单位,它是比进程更小的能独立运行的基本单位。
  • 一个进程可以创建和销毁多个线程,同一个进程中的多个线程可以并发执行
  • 一个程序至少有一个进程,一个进程至少有一个线程

3.并发和并行

  • 多线程程序在单核上运行,就是并发
  • 多个程程序在多核上运行,就是并行

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

并行:因为是在多个CPU上(比如有10个CPU),比如有10个线程,每个线程执行10毫秒(各自在不同CPU上执行),从人的角度看,这10个线程都在运行,但是从微观上看,在某一个时间点看,也同时有10个线程在执行,这就是并行

4.Go协程和Go主线程

Go主线程(有程序员直接称为线程/也可以理解成进程):一个Go线程上,可以起多个携程,你可以这样理解,携程是轻量的线程

Go协程的特点

有独立的栈空间

共享程序堆空间

调度由用户控制

携程是轻量级的线程

案例说明

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

1.在主线程(可以理解成进程)中,开启一个goroutine,该携程每隔1秒输出“hello,world”

2.在主线程中也每隔一秒输出“hello,golang”,输出10次后,退出程序

3.要求主线程和goroutine同时执行

4.画出主线程和协程执行流程图

代码实现

// 在主线程(可以理解成进程)中,开启一个goroutine,该协程每秒输出 “hello,world”
// 在主线程中也每隔一秒输出“hello,golang”,输出10次后,退出程序
// 要求主线程和goroutine同时执行
//编写一个函数,每隔1秒输出 “hello,world”
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)
	}
}

总结

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

MPG模式基本介绍

M:操作系统的主线程(是物理线程)

P:协程执行需要的上下文

G:协程

5.设置Golang运行的CPU数

介绍:为了充分利用多CPU的优势,在Golang程序中设置运行的CPU数目

 package main
 import "fmt"
 import "runtime"
func main(){
	// 获取当前系统CPU的数量
	num := runtime.NumCPU()
	// 这里设置num-1的CPU运行go程序
	runtime.GOMAXPROCS(num)
	fmt.Println("num=",num)
}
  • go1.8后,默认让程序运行在多个核上,可以不用设置了
  • go1.8前,还是要设置一下,可以更高效的利用CPU

6.channel(管道)看需求

需求:现在要计算 1-200的各个数的阶乘,并且把各个数的阶乘放入到map中。最后显示出来。要求使用goroutine完成

分析思路:

使用goroutine来完成,效率高,但是会出现并发/并行安全问题

这里就提出了不同goroutine如何通信的问题

代码实现

使用goroutine来完成(看看使用gorotine并发完成会出现什么问题?然后我们会去解决)

在运行某个程序时,如何知道是否存在资源竞争问题,方法很简单,在编译该程序时,增加一个参数 -race即可

不同goroutine之间如何通讯

1.全局变量的互斥

2.使用管道channel来解决

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

  • 英文没有对全局变量m加锁,因此会出现资源争夺问题,代码会出现错误,提示concurrent map writes
  • 解决方案:加入互斥锁
  • 我们的数的阶乘很大,结果会越界,可以将求阶乘改成sum += uint64(i)

源码

package main
import (
	"fmt"
	"time"
	"sync"
)
// 需求:现在要计算 1-200的各个数的阶乘,并且把各个数的阶乘放入到map中
// 最后显示出来。要求使用goroutine完成
// 思路
// 1. 编写一个函数,来计算各个数的阶乘,并放入到map中
// 2. 我们启动的协程多个,统计的将结果放入到map中
// 3. map应该做出一个全局的
var (
  myMap = make(map[int]int,10)
  // 声明一个全局的互斥锁
  // lock 是一个全局的互斥锁
  //sync 是包:synchornized 同步
  // Mutex: 是互斥
  lock sync.Mutex
)
// test函数就是计算n!,让将这个结果放入到myMap
func test(n int){
	res := 1
	for i := 1;i<=n;i++{
		res *= i
	}
	// 这里我们将res放入到myMap
	// 加锁
	lock.Lock()
	myMap[n] = res  // concurrent map writes?
	// 解锁
	lock.Unlock()
}
func main(){
	// 我们这里开启多个协程完成这个任务[200个]
	for i := 1;i<=20;i++{
		go test(i)
	}
	// 休眠10秒钟【第二个问题】
	time.Sleep(time.Second * 10)
	lock.Lock()
	// 这里我们输出结果 变量这个结果
	for i,v := range myMap{
		fmt.Printf("map[%d]=%d\n",i,v)
	} 
	lock.Unlock()
}

channel(管道)-基本使用

channel初始化

说明:使用make进行初始化

var intChan chan int

intChan = make(chan int,10)

向channel中写入(存放)数据

var intChan chan int

intChan = make(chan int,10)

num := 999

intChan <-10

intChan <-num

管道的初始化,写入数据到管道,从管道读取数据及基本的注意事项

package main
import (
	"fmt"
)
func main(){
	// 演示一下管道的使用
	// 1.创建一个可以存放3个int类型的管道
	var intChan chan int
	intChan = make(chan int,3)
	// 2.看看intChannel是什么
	fmt.Printf("intChan 的值=%v intChan本身的地址=%p\n",intChan,&intChan)
	// 3.向管道写入数据
	intChan<- 10
	num := 211
	intChan<- num
	// 注意点,当我们给管写入数据时,不能超过其容量
	intChan<- 50
	// intChan<- 98
	//4. 看看管道的长度和cap(容量)
	fmt.Printf("channel len=%v cap=%v \n",len(intChan),cap(intChan)) // 2,3
	// 5.从管道中读取数据
	var num2 int 
	num2 = <-intChan
	fmt.Println("num2=",num2)
	fmt.Printf("channel len=%v cap=%v \n",len(intChan),cap(intChan)) // 2,3
	// 6.在没有使用协程的情况下,如果我们的管道数据已经全部取出,再取就会报告 deadlock
	num3 := <-intChan
	num4 := <-intChan
	// num5 := <-intChan
	fmt.Println("num3=",num3,"num4=",num4)//,"num5=",num5)
}

channel使用的注意事项

1.channel中只能存放指定的数据类型

2.channel的数据放满后,就不能再放入了

3.如果从channel取出数据后,可以继续放入

4. 在没有使用协程的情况下,如果channel数据取完了,再取,就会报dead lock

示例代码

package main
import (
	"fmt"
)
type Cat struct{
	Name string
	Age int
}
func main(){
	// 定义一个存放任意数据类型的管道  3个数据
	// var callChan chan interface{}
	allChan := make(chan interface{},3)
	allChan<- 10
	allChan<- "tom jack"
	cat := Cat{"小花猫",4}
	allChan<- cat
	// 我们希望获得到管道中的第三个元素,则先将前2个推出
	<-allChan
	<-allChan
	newCat := <-allChan // 从管道中取出的Cat是什么?
	fmt.Printf("newCat=%T,newCat=%v\n",newCat,newCat)
	// 下面的写法是错误的!编译不通过
	// fmt.Printf("newCat.Name=%v",newCat.Name)
	// 使用类型断言
	a := newCat.(Cat)
	fmt.Printf("newCat.Name=%v",a.Name)
}

channel的关闭

使用内置函数close可以关闭channel,当channel关闭后,就不能再向channel写数据了,但是仍然可以从该channel读取数据

channel的遍历

channel支持for-range的方式进行遍历,请注意两个细节

  • 在遍历时,如果channel没有关闭,则会出现deadlock的错误
  • 在遍历时,如果channel已经关闭,则会正常遍历数据,遍历完后,就会退出遍历。

代码演示:

package main
import (
	"fmt"
)
func main(){
	intChan := make(chan int,3)
	intChan<- 100
	intChan<- 200
	close(intChan) // close
	// 这是不能够再写入到channel
	// intChan<-300
	fmt.Println("okook~")
	// 当管道关闭后,读取数据是可以的
	n1 := <-intChan
	fmt.Println("n1=",n1)
	// 遍历管道
	intChan2 := make(chan int,100)
	for i := 0; i< 100;i++{
		intChan2<-i*2 // 放入100个数据到管道
	}
	// 遍历管道不能使用普通的for循环
	// 在遍历时,如果channel没有关闭,则会出现deadlock的错误
	// 在遍历时,如果channel已经关闭,则会正常遍历数据,遍历完后,就会退出遍历
	close(intChan2)
	for  v := range intChan2{
		fmt.Println("v=",v)
	}
}

到此这篇关于Golang中goroutine和channel使用介绍深入分析的文章就介绍到这了,更多相关Go goroutine与channel内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

您可能感兴趣的文档:

--结束END--

本文标题: Golang中goroutine和channel使用介绍深入分析

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

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

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

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

下载Word文档
猜你喜欢
  • Golang中goroutine和channel使用介绍深入分析
    目录1.goroutine-看一个需求2.进程和线程介绍3.并发和并行4.Go协程和Go主线程5.设置Golang运行的CPU数6.channel(管道)看需求1.goroutine...
    99+
    2023-01-13
    Go goroutine与channel Go goroutine Go channel
  • Golang通道channel使用源码分析
    这篇文章主要介绍了Golang通道channel使用源码分析的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Golang通道channel使用源码分析文章都会有所收获,下面我们一起来看看吧。前言channel是g...
    99+
    2023-07-04
  • Go语言中的Goroutine和channel怎么使用
    这篇“Go语言中的Goroutine和channel怎么使用”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“Go语言中的Gor...
    99+
    2023-07-05
  • Golang反射介绍及应用场景分析
    go 语言中的反射功能允许程序在运行时检查和修改类型的结构。通过使用 type、value 和 reflect.kind,我们可以获取对象的类型信息、字段值和方法,还可以创建和修改对象。...
    99+
    2024-04-03
    golang 反射
  • Golang函数库的深入研究和分析
    go 函数库提供了丰富的内置函数,包括:fmt:用于格式化和打印数据;io:用于输入/输出操作;math:提供了数学函数和常量;net:用于网络连接和服务器功能;os:用于与操作系统交互...
    99+
    2024-04-19
    golang 函数库
  • Golang中的错误处理深入分析
    目录一、Go的内建类型error二、怎么判断一个错误值具体代表那一类错误知道错误类型的所属范围知道错误变量是哪几个值三、错误值体系的两种方法立体的-错误类型体系扁平的-错误值列表一、...
    99+
    2023-01-09
    Go错误处理 GoLang错误处理
  • 深入解析Golang中锁的原理和应用
    Golang中锁的原理及应用解析引言在并发编程中,常常会遇到多个 goroutine 同时访问共享资源的情况,一旦多个 goroutine 同时对共享资源进行读写操作,可能导致数据不一致性或者产生竞态条件。为了解决这个问题,Golang 提...
    99+
    2023-12-28
    Golang中锁的原理:锁机制 Golang中锁的应用:并发编程 Golang中锁的解析:互斥锁
  • 深入解析Golang中接口的定义和作用
    Golang中接口的定义和作用详解 引言 Golang是一门现代化的编程语言,它通过接口实现了面向对象编程的特性。在Golang中,接口是一种定义行为的方法集合,是一种约定,用于指定对象的行为。接口的定义和使...
    99+
    2024-01-24
    接口 Golang 作用详解
  • ReactcreateElement方法使用原理分析介绍
    目录摘要1.创建方法2.处理type3.处理config4.处理children5.对比真正的React.createElement源码摘要 在上一篇说过,React创建元素有两种方...
    99+
    2024-04-02
  • Golang排序和查找使用方法介绍
    目录排序的介绍交换式排序法二维数组的介绍二维数组的应用场景二维数组快速入门二维数组的使用排序的介绍 排序是将一组数据,依指定的顺序进行排列的过程。 排序的分类: 1.内部排序: 指将...
    99+
    2022-12-16
    Golang排序和查找 Golang查找 Golang排序
  • golang RPC包原理和使用详细介绍
    目录工作流程工作模式http模式服务器模式本篇文章旨在通过学习rpc包和github上的一个rpc小项目,熟悉和学习golang中各个包的使用 工作流程 通过阅读官方文档,了解了rp...
    99+
    2024-04-02
  • Vueextend使用示例深入分析
    目录一、使用场景二、补充组件注册三、深度解析Vue.extend()虽然已近用过很多次了,但都没有深入思考过这个东西,仔细想了想,有点意思 一、使用场景 按需使用组件,也叫懒加载,性...
    99+
    2024-04-02
  • Golang安装和使用protocol-buffer流程介绍
    目录前言安装protoc编译工具编写proto文件生成指定语言的proto文件调用proto制作插件前言 protocol buffer是Google发布的一种独立的数据交换格式,类...
    99+
    2024-04-02
  • Reactthis.setState方法使用原理分析介绍
    目录摘要1.异步的setState2.多个setState方法3.手动实现mySetState摘要 这一篇文章,主要是简单的实现一下this.setState方法,为了实现该方法,就...
    99+
    2024-04-02
  • C#中async和await的深入分析
    目录大概理解深入分析await和Wait()的区别去掉Task.Run的Wait小结其他.Await();总结大概理解 查了一个小时的资料:async和await 发现这个大神的解释...
    99+
    2022-11-13
    c# async和await c#异步编程
  • 详细介绍golang中.()的使用方法
    Golang是一门非常流行的编程语言,在很多领域都有着广泛的应用。在开发过程中,很多时候我们需要将函数作为参数传递给其他函数,这时候就需要用到golang中的.()用法。本文将详细介绍golang中.()的使用方法。什么是.()用法?在go...
    99+
    2023-05-14
  • 重点介绍Golang方法的语法和使用
    Golang作为一门比较年轻的语言,在方法中也有自己独特的实现方式。本文将重点介绍Golang方法的语法和使用。一、方法定义Golang中可以为任何类型定义方法,包括引用类型和非引用类型。方法定义格式如下:func (t Type) met...
    99+
    2023-05-14
  • Go中Sync.Cond的介绍和使用
    本篇内容介绍了“Go中Sync.Cond的介绍和使用”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!1. 程序中的通信方式GO语言中有句名言:...
    99+
    2023-06-15
  • C++深入分析STL中map容器的使用
    目录1、map容器2、map容器原理3、map容器函数接口4、使用示例1、map容器 map是C++ STL的一个关联容器,它提供一对一的数据处理能力。其中,各个键值对的键和值可以是...
    99+
    2024-04-02
  • 深入了解SpringBoot中@ControllerAdvice的介绍及三种用法
    目录浅析@ControllerAdvice1.处理全局异常2.预设全局数据3.请求参数预处理浅析@ControllerAdvice 首先,ControllerAdvice本质上是一个...
    99+
    2023-02-06
    SpringBoot @ControllerAdvice用法 @ControllerAdvice用法 SpringBoot @ControllerAdvice
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作