0
点赞
收藏
分享

微信扫一扫

golang goroutine协程 和 channel管道

爱读书的歌者 2022-04-14 阅读 174
golang

文章目录

前言

进程和线程基本介绍

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

程序,进程和线程的关系图

在这里插入图片描述

并发和并行

  1. 多线程合成在单核上运行,就是并发
    在这里插入图片描述

  2. 多线程乘车在多核上运行,就是并行
    在这里插入图片描述
    总结:
    并发:
    因为是在一个cpu上,比如有10个线程,每个线程执行10毫秒(进行轮询操作),从人的角度看,好像这个10个线程都在运行,但是从微观上看,在某一个时间点看,其实只有一个线程在执行,这就是并发
    并行:
    因为是在多个cpu上(比如有10个cpu),比如有10个进程,每个线程执行10毫秒(各自在不同cpu上执行),从人的角度看,这个10个线程都在运行,但是从微观上看,在某一个时间点看,也同时有10个线程在执行,这就是并行

Go协程和Go主线程

  1. Go主线程(有程序员直接称为线程/也可以理解成进程):一个Go线程上,可以起多个协程,你可以这样理解,协程就是轻量级的线程
  2. Go协程的特点
    1. 有独立的栈空间
    2. 共享程序堆空间
    3. 调度由用户控制
    4. 协程是轻量级的协程
      在这里插入图片描述

goroutine 快速入门

案例一

package main

import(
	"fmt"
	"time"
	"strconv"
)

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

//编写一个函数,每个一秒输出 ”hello,world“
func test(){
	for i := 1; i <= 10; i++ {
		fmt.Println("test() hello,world " + strconv.Itoa(i))
		time.Sleep(time.Second) //宕机一秒
	}
}

func main(){

	//普通方式
	// test()
	/*
	结果
	test() hello,world 1
	test() hello,world 2
	....
	test() hello,world 10
	main() hello,golang 1
	main() hello,golang 2
	....
	main() hello,golang 10
	普通方式只有test()执行完成后才会执行主进程for
	*/

	go test()//开启一个协程
	/*
	main() hello,golang 1
	test() hello,world 1
	test() hello,world 2
	main() hello,golang 2
	main() hello,golang 3
	test() hello,world 3
	....
	test() hello,world 10
	main() hello,golang 10
    开启协程后会同时执行 
	*/

	for i := 1; i <= 10; i++ {
		fmt.Println("main() hello,golang " + strconv.Itoa(i))
		time.Sleep(time.Second)
	}
}

在这里插入图片描述

MPG模式基本介绍

  1. M:操作系统的主线程(是物理线程)
  2. P:协程执行需要的上下文
  3. G:协程

设置Golang 运行的CPU

package main

import(
	"fmt"
	"runtime"
)

func  main()  {
	//NumCPU返回本地机器的逻辑CPU个数
	cpuNum := runtime.NumCPU()

	//可以自己设置使用多个CPU
	runtime.GOMAXPROCS(cpuNum - 1)
	fmt.Printf("cpuNum=%v",cpuNum)
	//结果cpuNum=12
}

channel(管道) 快速入门

案例一

package main

import(
	"fmt"
)

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

// 思路
// 1. 编写一个函数,来计算各个数的阶乘,并且放入到 map中
// 2. 启动多个协程,将统计的结果放入到 map中
// 3. map 应该做出一个全局

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

// test 函数 计算 n,将这个结果放入到map中
func test(n int){
  res := 1
  for i := 1; i <= n; i++ {
	res *= i 
  }

  //将结果放入map中
  myMap[n] = res
}

func main(){
   //开启协程 开启200个协程完成这个任务
   for i := 1; i <= 200; i++ {
	   go test(i)
   }

   //输出结果
   for k, v := range(myMap){
	   fmt.Printf("myMap[%d] = %d\n",k,v)
   }
   /*
    错误:
    fatal error: concurrent map writes
	fatal error: concurrent map iteration and map write
	fatal error: concurrent map writes
	错误为 并发映射输入
   */
}

在编译该程序时,郑家一个参数 -race 在运行时也会提示一些错误 看演示

在这里插入图片描述

解决方法

  1. 全局变量加锁同步
  2. channel

全局变量加锁同步

package main

import(
	"fmt"
	"sync"
	"time"
)

var (
	myMap = make(map[int]int,10)
	//声明一个全局的互斥锁
	//lock 是一个全局的互斥锁
	lock sync.Mutex
)

// test 函数 计算 n,将这个结果放入到map中
func test(n int){
  res := 1
  for i := 1; i <= n; i++ {
	res *= i 
  }

  //将结果放入map中
  //加锁
  lock.Lock()
  myMap[n] = res
  //解锁
  lock.Unlock()
}

func main(){
   //开启协程 开启200个协程完成这个任务
   for i := 1; i <= 200; i++ {
	   go test(i)
   }

   //休眠10秒钟
   time.Sleep(time.Second * 10)
   
   //输出结果
   //加锁
   lock.Lock()
   for k, v := range(myMap){
	   fmt.Printf("myMap[%d] = %d\n",k,v)
   }
  //解锁
  lock.Unlock()
  /*
  结果:
    myMap[29] = -7055958792655077376
	myMap[37] = 1096907932701818880
	myMap[44] = 2673996885588443136
	myMap[50] = -3258495067890909184
	myMap[61] = 3098476543630901248
	myMap[123] = 0
	myMap[136] = 0
	myMap[139] = 0
	myMap[143] = 0
	结果成功 没有提示并发插入数据
  */

}

channel基本介绍

为什么需要channel
前面使用全局变量加锁同步解决goroutine的通讯,但不完美

  1. 主线程在等待所有goroutine全部完成的时间很难确定,我们这里设置了10秒,仅仅是估算
  2. 如果主线程休眠时间长了,会加长等待时间,如果等待时间短了,可能还有goroutine处于工作状态,这时也会随着主线程的退出而销毁
  3. 通过全局变量加锁同步来实现通讯,也并不利于多个协程对全局变量的读写操作
  4. 上面种种分析都在呼唤一个新的通讯机制 channel

channel的介绍

  1. channel本质就是一个数据结构-队列 如下图
    在这里插入图片描述
  2. 数据是先进先出
  3. 线程安全,多个goroutine访问时,不需要加锁
  4. channel是有类型的,一个string的channel只能存放string类型数据

待续。。。。

举报

相关推荐

0 条评论