meowrain 发布于 收录于 Go Golang 并发 (三) 基本同步原语 Go 语言在 sync 包中提供了用于同步的一些基本原语,包括常见的互斥锁 Mutex 和读写互斥锁 RWMutex 以及 Once,WaitGroup. 这些基本原语的主要作用是提供较为基础的同步功能
Mutex Mutex 是 golang 标准库的互斥锁,主要用来处理并发场景下共享资源的访问冲突问题。
Mutex 互斥锁在 sync 包中,它由两个字段 state 和 sema 组成,state 表示当前互斥锁的状态,而 sema 真正用于控制锁状态的信号量,这两个加起来只占 8 个字节空间的结构体就表示了 Go 语言中的互斥锁。
1 2 3 4 type Mutex struct { state int32 sema uint32 } 互斥锁的作用,就是同步访问共享资源。互斥锁这个名字来自互斥 (mutual exclusion) 的概念,互斥锁用于在代码上创建一个临界区,保证同一个时间只有一个 goroutine 可以执行这个临界区代码。
不使用互斥锁:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 package main import ( "fmt" "sync" ) var ( counter int wg sync.
meowrain 发布于 收录于 Go Golang 并发(二) 参考 链接:https://juejin.cn/post/7075723579303657485
GMP 模型 Goroutine 是 Go 语言中的协程,实现了轻量级并发,和传统的线程相比,Goroutine 有以下特点:
轻量级 高效调度:Go 运行时在用户态进行调度,避免了频繁的上下文切换带来的开销,使得调度更加高效 GMP 模型基本概念 G: Goroutine
M: Machine
P: Processer
Goroutine 是 Go 语言中的协程,代表一个独立的执行单元,Goroutine 比线程更加轻量级,启动一个 Goroutine 的开销非常小,Goroutine 的调度由 Go 运行时在用户态进行。
Machine: 代表操作系统的线程,实际执行 Go 代码,一个 M 可以执行多个 Goroutine,但是同一时间只能执行一个 Goroutine。M 和操作系统的线程直接对应,Go 运行时通过 M 来利用多核 CPU 的并行计算能力。
Processer: P 代表执行上下文,管理者可运行的 Goroutine 队列,并且负责和 M 进行绑定,P 的数量决定了可以并行执行 Goroutine 的数量。Go 运行时会根据系统的 CPU 核数设置 P 的数量。
在 GMP 模式中,线程是物理生产者,调度器会将 goroutine 分派给一个线程。
说明:
全局队列:要执行 goroutines 的队列 P 本地队列: 和全局队列一样,它包含要执行的 goroutine,但是这个队列的最大容量是 256.
meowrain 发布于 收录于 Go Golang 并发(一) Golang 基础之并发知识 (一) 大家好,今天将梳理出的 Go 语言并发知识内容,分享给大家。 请多多指教,谢谢。 本次 - 掘金
什么是并发,并行 什么是原子操作 原子操作可以理解为: 在进行过程中不能被中断的操作
也就是说,针对某个值的原子操作在被进行的过程中,CPU 绝不会再去进行其它针对该值的操作,无论这些其它的操作是否为原子操作都会是这样
为了实行这样的严谨性,原子操作仅会由一个独立的 CPU 指令代表和完成,只有这样才能够在并发环境下保证原子操作的绝对安全。
Go 语言提供的原子操作都是非侵入式的,它们由标准库代码包 sync/atomic 中的众多函数代表
什么是并发锁 如果两个或者多个线程在没有互相同步的情况下,访问某个共享的资源,并且试图同时读和写这个资源,就处于互相竞争的状态,这种情况被称为 race condition 也就是竞态。
竞态状态的存在是让并发程序变得复杂的地方,十分容易引起潜在问题。
对于一个共享资源的读和写必须是原子化,可以理解为,同一时刻只能有一个线程对共享资源进行读和写的操作。
一种防止竞态状态的方法,就是使用锁机制,锁住共享资源,保证线程的同步状态。
什么是通道 Go 提供了一种称为通道的机制,通道 (chan) 是 goroutine 之间传递特定值的通信机制,用于在 goroutine 之间共享数据。当作为 goroutine 执行并发活动时,需要在 goroutine 之间共享资源或数据,通道充当 goroutine 之间的管道并提供一种机制来保证同步交换。
它属于 通信顺序进程并发模式 (Communicating sequential processes,CSP) 。
Go 语言中还有另一种并发模式,即共享内存多线程的传统模型 MPG。
meowrain 发布于 收录于 Go 什么是内存逃逸? 逃逸值得是在函数内部创建的对象或者变量,在函数结束后,仍然被其它部分引用或者持有
对内存管理的理解 堆内存一般来说是人为手动进行管理,手动申请分配和释放的。一般硬件内存有多大,堆内存就有多大,适合不可预知大小的内存分配,分配速度较慢,而且会形成内存碎片。(在 Go 语言中,因为 Go 有 GC 自动管理,所以程序员不需要手动释放内存)
栈内存是一种有特殊规则的线性表数据结构,由编译器进行管理,自动申请,分配和释放,大小一般是固定的,它的分配速度非常快,因为只需要移动栈顶指针
内存逃逸带来的影响 当对象或者变量等一些本应在站上分配的变量,因为其生命周期超出了函数范围,不得不分配到堆上。 这时候就会引起
性能开销 堆分配比栈分配慢 垃圾回收也会带来开销 内存碎片 堆内存很容易产生内存碎片 缓存局部性 栈内存通常具有更好的缓存局部性,因为栈上的数据通常是连续分配的,而且生命周期短。 堆内存的分配是分散的,可能导致缓存未命中的问题,影响性能。 导致内存逃逸的原因 变量的生命周期超出了栈内存的活动范围 编译时不知道变量大小,因为栈内存的分配需要编译器在编译时知道变量的大小和生命周期 编译时无法确定变量大小的情况:
如果变量的大小在编译时无法确定(例如动态数组、切片、映射等),编译器无法在栈上为其分配固定大小的内存。
此时,编译器会将这些变量分配到堆上,以确保程序能够正常运行。
Golang 的内存分片基本原则
指向栈上对象的指针不能被存储到堆中 指向栈上对象的指针不能超过该栈对象的生命周期
meowrain 发布于 收录于 Go Golang 切片原理 扩容规律 切片作为参数 Go 语言的函数参数传递,只有值传递,没有引用传递,切片作为参数也是如此
我们来验证这一点
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 package main import "fmt" func main () { sl := [] int {6, 6, 6} f (sl) fmt.Println (sl) } func f (sl [] int) { for i := 0; i < 3; i++ { sl = append (sl, i) } fmt.Println (sl) } 可以看到,输出的 sl 的值是不一样的,也就是说,f 函数没能修改主函数中的 sl 变量,而只是修改了形参 sl 变量的内容
当我们传递一个切片给函数的时候,函数接收到的其实是这个切片的一个副本,但是他们的 array 字段指向的是同一个底层数组。
这意味着,如果我们修改底层数组,是会影响到实参和形参的。
meowrain 发布于 收录于 Go Golang 数组与切片的区别 数组与切片的基本概念 1.1 数组的定义和初始化 数组是一种固定长度的数据结构,声明时需要指定长度。
1 2 3 4 5 6 7 8 package main import "fmt" func main () { var a [3] int = [3] int {1, 2, 3} fmt.Println (a) } 1.2 切片的定义和初始化 切片是一个动态的长度可变的序列,切片初始化与定义不需要指定长度,而是通过对数组的引用来创建
1 2 3 4 5 6 7 8 package main import "fmt" func main () { var a [] int = [] int {1, 2, 3, 4, 5, 6, 7} fmt.Println (a) } 数组与切片的区别 2.1 大小和长度 数组是大小固定的,声明时需要指定长度
切片的大小是可变的,长度可以随着元素的增加而增加,不需要在声明的时候指定长度
2.2 传递方式 数组和切片在作为参数传递的时候,都是值传递,但是因为切片和切片的副本的指向的底层数组是相同的。
2.3 初始化 数组需要指定长度并且逐个赋值,也可以使用字面量赋值
meowrain 发布于 收录于 Go Golang defer Golang defer 执行时机 defer 语句在声明时,会立即计算其参数的值并将函数与参数绑定,但函数体的执行会延迟到外层函数即将返回时。
如果在函数中有多个 defer,它们会按照后进先出的顺序执行,类似栈结构
看下面的例子
1 2 3 4 5 6 7 8 9 10 11 12 13 package main import "fmt" func example () { defer fmt.Println ("First") defer fmt.Println ("Second") fmt.Println ("Inner") } func main () { example () } defer 和匿名函数的一些问题和疑惑 直接声明 defer 匿名函数,参数不通过匿名函数的参数传入 这个其实就是 匿名函数捕获外部变量(闭包行为):
匿名函数会捕获变量的引用(即变量地址),而不是变量值。 如果变量在 defer 注册之后发生了变化,匿名函数使用的值也会随之变化。 1 2 3 4 5 6 7 8 9 10 11 12 package main import "fmt" func main () { x := 10 defer func () { fmt.
meowrain 发布于 收录于 前端 表单输入与绑定 v-model 使用
输入框 复选框 1 2 3 4 5 6 7 8 9 10 11 12 <script setup lang="ts"> import {ref,reactive} from 'vue'; const text = ref (false) </script> <template> <div> <p>isPressed: {{text}}</p> <input type="checkbox" v-model="text"> </div> </template> 复选框 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <script setup lang="ts"> import {ref,reactive} from 'vue'; const checkedNames = ref ([]) </script> <template> <div> <div>Checked names: {{ checkedNames }}</div> <input type="checkbox" value="Jack" v-model="checkedNames" /> <label for="jack">Jack</label> <input type="checkbox" value="John" v-model="checkedNames" /> <label for="john">John</label> </div> </template> 单选按钮 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <script setup lang="ts"> import {ref,reactive} from 'vue'; const picked = ref () </script> <template> <div> <div>Picked: {{ picked }}</div> <input type="radio" id="one" value="One" v-model="picked" /> <label for="one">One</label> <input type="radio" id="two" value="Two" v-model="picked" /> <label for="two">Two</label> </div> </template> 选择器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <script setup lang="ts"> import {ref,reactive} from 'vue'; const selected = ref () </script> <template> <div> <div>selected: {{selected}}</div> <select v-model="selected" > <option disabled>Please select one</option> <option selected>A</option> <option>B</option> <option>C</option> <option>D</option> </select> </div> </template> 使用 v-for 动态渲染
meowrain 发布于 收录于 前端 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 import type { AxiosInstance, AxiosResponse, InternalAxiosRequestConfig, } from "axios"; import axios from "axios"; declare module "#app" { interface NuxtApp { $axios: AxiosInstance; } } const handleRequestHeader = (config: InternalAxiosRequestConfig<any>) => { const token = localStorage.
meowrain 发布于 收录于 前端 AXIOS 请求执行过程 请求流程和拦截器 代码 封装好
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 import type {AxiosInstance, AxiosResponse, InternalAxiosRequestConfig,} from "axios"; import axios from "axios"; const handleRequestHeader = (config: InternalAxiosRequestConfig<any>) => { const token = localStorage.