Golang笔记——goroutine(协程)

goroutine-看一个需求

  • 需求:要求统计 1-9000000000 的数字中,哪些是素数?
  • 分析思路:
  1. 传统的方法,就是使用一个循环,循环的判断各个数是不是素数。[很慢]
  2. 使用并发或者并行的方式,将统计素数的任务分配给多个 goroutine 去完成,这时就会使用到
    goroutine.【速度提高 4 倍】

goroutine-基本介绍

进程和线程介绍
在这里插入图片描述
程序、进程和线程的关系示意图
在这里插入图片描述

并发和并行

  1. 多线程程序在单核上运行,就是并发
  2. 多线程程序在多核上运行,就是并行
  3. 示意图:
    在这里插入图片描述
  • 小结
    在这里插入图片描述

Go 协程和 Go 主线程

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

Go 协程的特点

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

一个示意图
在这里插入图片描述

goroutine-快速入门

案例说明
请编写一个程序,完成如下功能:

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

代码实现
在这里插入图片描述
输出的效果说明, main 这个主线程和 test 协程同时执行
在这里插入图片描述
主线程和协程执行流程图
在这里插入图片描述
快速入门小结

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

goroutine 的调度模型

MPG 模式基本介绍
在这里插入图片描述
MPG 模式运行的状态 1
在这里插入图片描述

MPG 模式运行的状态 2
在这里插入图片描述

设置 Golang 运行的 cpu 数

介绍:为了充分了利用多 cpu 的优势,在 Golang 程序中,设置运行的 cpu 数目
在这里插入图片描述

channel(管道)-看个需求

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

分析思路:

  1. 使用 goroutine 来完成,效率高,但是会出现并发/并行安全问题.
  2. 这里就提出了不同 goroutine 如何通信的问题

代码实现

  1. 使用 goroutine 来完成(看看使用 gorotine 并发完成会出现什么问题? 然后我们会去解决)
  2. 在运行某个程序时,如何知道是否存在资源竞争问题。 方法很简单,在编译该程序时,增加一 个参数 -race 即可 [示意图]
  3. 代码实现:
package main 
import (
	"fmt"
	"time"
)
	
	// 需求:现在要计算 1-200  的各个数的阶乘,并且把各个数的阶乘放入到 map 中。
	// 最后显示出来。要求使用 goroutine 完成
	
	// 思路
	// 1.  编写一个函数,来计算各个数的阶乘,并放入到 map 中.
	// 2.  我们启动的协程多个,统计的将结果放入到 map 中
	// 3. map 应该做出一个全局的.


var (
	myMap = make(map[int]int, 10)
)

// test 函数就是计算 n!, 让将这个结果放入到myMap 
func test(n int) {
	res := 1
	for i := 1; i <= n; i++ { 
		res *= i
	}
	//这里我们将 res 放入到 myMap 
	myMap[n] = res //concurrent map writes?
}
	
func main() {
	// 我们这里开启多个协程完成这个任务[200 个] 
	for i := 1; i <= 200; i++ {
		go test(i)
	}
	//休眠 10 秒钟【第二个问题 】
	time.Sleep(time.Second * 10)

	//这里我们输出结果,变量这个结果 
	for i, v := range myMap {
		fmt.Printf("map[%d]=%d\n", i, v)
	}
}

在这里插入图片描述
在这里插入图片描述
4) 示意图:
在这里插入图片描述
不同 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 同步
	//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 <= 200; 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()
}

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

浩波的笔记

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值