【无标题】

记录一次go 高并发学习过程

进程

定义:

  • 进程是系统进行资源分配和调度的基本单位
  • 是程序执行的实体
  • 是线程的容器
  • 不同拥有自己独立的内存空间
    进程切换:
    在这里插入图片描述

线程:

定义:

  • 线程是OS能调度的最小单位
  • 大部分情况下包含在进程中
  • 同一线程中的进程共享同一资源:虚拟地址空间、文件描述符、信号处理等,但同一进程的多条线程有各自的调用栈、寄存器、线程本地存储。
  • 线程间共享同进程的共享内存空间
    线程切换过程:

在这里插入图片描述

线程池

  • 线程过多导致额外开销(创建、销毁、调度),使用线程池来维护多进程、等待任务分配
  • 一般是生产消费模型,由生产者(生成工作任务、存入队列)、消费者(取出任务进行处理,没有则挂起)、任务队列组成 。
  • 线程池分为:核心线程(线程池启动时就创建,长期存在,无限期等待任务)、非核心线程(任务数超出核心线程负荷时创建,创建后没有新任务就会回收掉)

协程

为什么有了线程还需要协程

  • 多线程开发设计变得更复杂、需要考虑多同步竞争问题、如锁、竞争冲突
  • 多线程创建也需要大量内存,虚拟内存也需要大约4MB

定义:

  • 用户视角的一种抽象,os没有协程概念
  • 本质时在单线程内运行,在线程内进行切换
  • 拥有自己的上下文和栈,协程调度切换时保留寄存器上下文和栈,切回来时恢复
  • goroutine是golang中对协程的实现
  • goroutine底层实现了少量线程干多事,减少切换时间等

协程切换:
在这里插入图片描述

goroutine

定义:

  • 是Go语言的一种机制,GO语言协程中的实现
  • 语言中内置了调度和切换上下文机制,可将goroutine中任务合理分配给CPU

GPM

  • 是Go自己实现的一套调度系统
  • 全局队列存放着待运行的goroutine
    在这里插入图片描述
  • p:管理着一组Gorutine队列,容量有限,只能放256个,新建G时优先放入P队列,队列满了放入全局队列
  • P 里面会存储当前G的上下文环境(函数指针、堆栈地址、地址边界),P会对自己管理的G队列进些调度,当自己队列消费完了,就去全局队列里取。如果全局队列也消费完了,就去其他P里抢任务。

代码练习

test1

/*
*

	@author: tangjing
	@date: 2023/6/4
	@note:

*
主线程开启goroutine 每隔1S输出 "你好唐经"
主线程中每隔 2s 输出 go routine 十次后退出
主线程和 goroutine同时执行
*/
package main

import (
	"fmt"
	"time"
)

/*
*
协程 在main方法执行完后就结束
go 1.8之前要设置CPU核心数, runtime.GOMAXPROC(16),1.8后全开
查看核心数 runtime.NumCPU()
*/
func main() {
	go runTimes(10) //直接加协程
	for i := 0; i < 10; i++ {
		fmt.Println("main", i, "剩余次数", 10-i)
		time.Sleep(time.Second * 2)
	}
}

func runTimes(times int) int {
	for i := 0; i < times; i++ {
		fmt.Println("runTimes", i, "剩余次数", times-i)
		time.Sleep(time.Second)
	}
	return times
}

互斥锁解决资源竞争问题:

/*
*

	@author: tangjing
	@date: 2023/6/4
	@note:

*
MAP 并发写:
直接运行:报错 fatal error: concurrent map writes
go build -race main.go && go build main.go检测数据竞争状态,再次执行会提示 WA
解决方案:

	1、互斥锁
		全局变量 通过加锁lock unlock 的方法达到线程安全
		lock sync.Mutex
			lock.lock()等使用完 lock.Unlock()
	2、channel通道
*/
package main

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

var (
	testMap = make(map[int]int, 10)
	lock    sync.Mutex
)

func testNum(num int) {
	lock.Lock()
	res := 1
	for i := 1; i <= num; i++ {
		res *= i
	}
	testMap[num] = res
	lock.Unlock()
}

func main() {
	start := time.Now()
	for i := 1; i < 20; i++ {
		go testNum(i)
	}
	//协程要在main之后完毕
	time.Sleep(time.Second * 5)

	//注意  这里也存在竞争关系,因为 在读取testMap 时,仍然有资源在写入testMap
	for key, value := range testMap {
		fmt.Printf("数字 %v ,对应的阶乘是 %v\n", key, value)
	}
	end := time.Since(start)
	fmt.Println(end)
}

channel 的使用

在这里插入代码片/*
*

	@author: tangjing
	@date: 2023/6/5
	@note:

channel 本质是队列 FIFO , 线程安全
attention: channel是有类型的, 。当然如果传空接口就能接受所有类型
定义/声明Channel :  var intChan chan int
需要make之后才可以使用  intChan = make (chan int,6)
*
*/
package main

import "fmt"

var intChan = make(chan int, 10)

func main() {
	intChan <- 1
	intChan <- 2

	fmt.Println(intChan) // 打印出来是指针地址
	fmt.Printf("intChan的取出来第一个值是%v,地址是%v\n\n", <-intChan, &intChan)
	fmt.Printf("intChan的大小-1是%v,容量是%v\n\n", len(intChan), cap(intChan))
	<-intChan
	//num := <-intChan
	//如果队列被取空还在取  会产生死锁问题,当然  如果超出范围还在插也会死锁
	//fmt.Printf("num = %v", num)

	mapChan := make(chan map[int]string, 5)
	map1 := make(map[int]string, 2)
	map1[1] = "tangjing"
	map1[2] = "jing"
	mapChan <- map1

	allChan := make(chan interface{}, 5)
	allChan <- Dog{Name: "阿花", Color: "小黄"}
	allChan <- 1
	allChan <- 2

	dog1 := <-allChan
	fmt.Printf(" %T \n", dog1)
	//fmt.Println("%T",dog1.Color) 直接运行报错,因为编译阶段识别出来的类型是interface{} 而不是Dog类型
	//类型断言(Type Assertion)是一个使用在接口值上的操作,用于检查接口类型变量所持有的值是否实现了期望的接口或者具体的类型。value, ok := x.(T)
	a, _ := dog1.(Dog)
	fmt.Printf(" %T \n", a.Color)
}

type Dog struct {
	Name  string
	Color string
}
/*
*

	@author: tangjing
	@date: 2023/6/5
	@note:

channel的循环遍历与关闭
for range循环取值 需要close(chanName)
管道关闭后 不能写入
否则 fatal error: all goroutines are asleep - deadlock! 死锁

*
*/
package main

import "fmt"

func main() {
	allChan := make(chan interface{}, 5)
	allChan <- Dog1{Name: "阿花", Color: "小黄"}
	allChan <- 1
	allChan <- 2

	//close(allChan) //如果不关闭就循环 则会存在问题
	for val := range allChan {
		fmt.Printf("%v\n", val)
	}

}

type Dog1 struct {
	Name  string
	Color string
}

思路:
在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值