searchusermenu
  • 发布文章
  • 消息中心
点赞
收藏
评论
分享
原创

golang应知必会的基础知识

2023-09-25 09:56:33
29
0

1、golang中的GMP模型

      GPM 模型(Goroutine,Machine,Processor),G代表Goroutine,M代表Machine, P代表Processor。

       GPM模型中有一个全局队列(Global Queue):存放等待运行的 G,还有一个 P 的本地队列:也是存放等待运行的 G,但数量有限,不超过 256 个。GPM 的调度流程从 go func()开始创建一个 goroutine,新建的 goroutine 优先保存在 P 的本地队列中,如果 P 的本地队列已经满了,则会保存到全局队列中。

       M 会从 P 的队列中取一个可执行状态的 G 来执行,如果 P 的本地队列为空,就会从其他的 MP 组合偷取一个可执行的 G 来执行,当 M 执行某一个 G 时候发生系统调用或者阻塞,M 阻塞,如果这个时候 G 在执行,runtime 会把这个线程 M 从 P 中摘除,然后创建一个新的操作系统线程来服务于这个 P,当 M 系统调用结束时,这个 G 会尝试获取一个空闲的 P 来执行,并放入到这个 P 的本地队列,如果这个线程 M 变成休眠状态,加入到空闲线程中,然后整个 G 就会被放入到全局队列中。

       关于 G,P,M 的个数问题,G 的个数理论上是无限制的,但是受内存限制,P 的数量一般建议是逻辑 CPU 数量的 2 倍,M 的数据默认启动的时候是 10000,内核很难支持这么多线程数,所以整个限制客户忽略,M 一般不做设置,设置好 P,M 一般都是要大于 P。

2、golang的内存管理

         golang的内存管理蛮有意思的,把对象放在一个个的span中,不同大小的对象放在不同的span中,有64种。Go语言内存划分的三个区域分别为:arena区,bitmap区,spans区。对象分配在arena区,基本存储单元为span。bitmap区记录了GC和对象是否包含指针的标志位,spans记录了span索引指针,便于查找。

3、 golang中的make和new关键字

 new 对指针类型分配内存,返回值是分配类型的指针,new也可以对 slice 、map、channel 分配内存;

 make 仅用于 slice、map和 channel 的初始化,返回值为类型本身,而不是指针;new(T)为每个类型分配一片内存,目前看来new(T)并不常用。

4、 golang中的测试方法

golang的测试方法包含:

单元测试、性能测试,都是在testing这个包下面。单元测试是testing.T 和性能测试 testing.B

测试有几个约定需要遵守:

1.一般测试func TestXxx(*testing.T)

测试行必须Test开头,Xxx为字符串,第一个X必须大写的[A-Z]的字母,一般Xxx为被测试方法的函数名。

2.性能测试func BenchmarkXxx(*testing.B)

性能测试用Benchmark标记,Xxx同上。

3. 测试文件名约定

go语言测试文件名约定规则是必须以_test.go结尾,放在相同包下。

举个栗子:这是一个主函数,定义了加法Sum运算。

package main
import “testing”
func TestSum(t *testing.T) {
if sum(1,2) != 3 {
t.Error(“test foo:Sum failed”)
} else {
t.Log(“test foo:Sum pass”)
}
}
运行的话go test就行了。

性能测试代码

package main
import “testing”
func BenchmarkSum(b *testing.B) {
for i := 0; i < b.N; i++ {
sum(1, 2)
}
}

5、 golang中的channel的数据结构

     channel 的数据结构包含 qccount 当前队列中剩余元素个数,dataqsiz 环形队列长度,即可以存放的元素个数,buf 环形队列指针,elemsize 每个元素的大小,closed 标识关闭状态,elemtype 元素类型,sendx 队列下表,指示元素写入时存放到队列中的位置,recv 队列下表,指示元素从队列的该位置读出。recvq 等待读消息的 goroutine 队列,sendq 等待写消息的 goroutine 队列,lock 互斥锁,chan 不允许并发读写。       

      channel 的几个特点 1)、读写值 nil 管道会永久阻塞 2)、关闭的管道读数据仍然可以读数据 3)、往关闭的管道写数据会 panic 4)、关闭为 nil 的管道 panic 5)、关闭已经关闭的管道 panic。

6. golang中select的用法

      golang 中的 select 就是用来监听和 channel 有关的 IO 操作,当 IO 操作发生时,触发相应的动作。有点像系统IO中的select、poll、epoll中的select ,select 只能应用于 channel 的操作,既可以用于 channel 的数据接收,也可以用于 channel 的数据发送。如果 select 的多个分支都满足条件,则会随机的选取其中一个满足条件的分支执行。
select {
    case <- chan1:
        // 如果 chan1 成功读到数据,则进行该 case 处理语句
    case chan2 <- 1:
        // 如果成功向 chan2 写入数据,则进行该 case 处理语句
    default:
        // 如果上面都没有成功,则进入default处理流程
}

7. Golang中的Map与Sync.Map

        原生的go Map在并发读写场景下经常会遇到 panic的情况。造成的原因是map是非线性安全的,并发读写过程中map的数据会被写乱。

        而一般情况下,解决并发读写 map 的思路是加锁,或者把一个 map 切分成若干个小 map,对 key 进行哈希。在业界中使用最多并发指出的模式分别是:  ①原生 map + 互斥锁 或者 读写锁。②  标准库 sync.Map (Go 1.9 及之后)。
        使用的用法如下,声明后调用相应的方法即可:

package main
import (
    "fmt"
    "sync"
)
func main() {
    var m sync.Map
    //Store
    m.Store(1,"a")
    m.Store(2,"b")
    //LoadOrStore
    //若key不存在,则存入key和value,返回false和输入的value
    v,ok := m.LoadOrStore("1","aaa")
    fmt.Println(ok,v) //false aaa
 
    //若key已存在,则返回true和key对应的value,不会修改原来的value
    v,ok = m.LoadOrStore(1,"aaa")
    fmt.Println(ok,v) //false aaa
    //Load
    v,ok = m.Load(1)
    if ok{
        fmt.Println("it's an existing key,value is ",v)
    } else {
        fmt.Println("it's an unknown key")
    }
    //Range
    //遍历sync.Map, 要求输入一个func作为参数
    f := func(k, v interface{}) bool {
        //这个函数的入参、出参的类型都已经固定,不能修改
        //可以在函数体内编写自己的代码,调用map中的k,v
            fmt.Println(k,v)
            return true
        }
    m.Range(f)
    //Delete
    m.Delete(1)
    fmt.Println(m.Load(1))
}

8.Golang的性能调优工具

      首先了解一个概念,profile。

       profile 就是对应用的画像,这里画像就是应用使用 CPU 和内存等情况,也就是说应用使用了多少 CPU 资源、都是哪些部分在使用、每个函数使用的比例是多少、有哪些函数在等待 CPU 资源等等。知道了这些,我们就能对应用进行规划,也能快速定位性能瓶颈。Golang 是一个对性能特别看重的语言,因此语言中自带了 profile 的库。

      golang在语言层面集成了profile采样工具,在程序运行过程中可以获取cpu、heap、block、traces等执行信息,这些会涉及到runtime/pprof、net/http/pprof、runtime/trace等package。

      一般情况下,获取profile数据最有两种形式:web形式与profile文件生成形式。

      具体用法就是引入包 net/http/pprof,然后在代码中

package main
import (
"log"
"net/http"
_ "net/http/pprof"
)
func main() {
// 性能分析
go func() {
log.Println(http.ListenAndServe(":8080", nil))
}()
// 实际业务代码
for {
Sum("test")
}
}
func Sum(str string) string {
data := []byte(str)
sData := string(data)
    var sum = 0
for i := 0; i < 10000; i++ {
    sum += i
}
return sData
}

其中,net/http/pprof包的init函数中注册了以下路由到http服务中,用浏览器打开localhost:8080/debug/pprof/,即可使用pprof提供的功能。

 

0条评论
0 / 1000
l****n
4文章数
0粉丝数
l****n
4 文章 | 0 粉丝
原创

golang应知必会的基础知识

2023-09-25 09:56:33
29
0

1、golang中的GMP模型

      GPM 模型(Goroutine,Machine,Processor),G代表Goroutine,M代表Machine, P代表Processor。

       GPM模型中有一个全局队列(Global Queue):存放等待运行的 G,还有一个 P 的本地队列:也是存放等待运行的 G,但数量有限,不超过 256 个。GPM 的调度流程从 go func()开始创建一个 goroutine,新建的 goroutine 优先保存在 P 的本地队列中,如果 P 的本地队列已经满了,则会保存到全局队列中。

       M 会从 P 的队列中取一个可执行状态的 G 来执行,如果 P 的本地队列为空,就会从其他的 MP 组合偷取一个可执行的 G 来执行,当 M 执行某一个 G 时候发生系统调用或者阻塞,M 阻塞,如果这个时候 G 在执行,runtime 会把这个线程 M 从 P 中摘除,然后创建一个新的操作系统线程来服务于这个 P,当 M 系统调用结束时,这个 G 会尝试获取一个空闲的 P 来执行,并放入到这个 P 的本地队列,如果这个线程 M 变成休眠状态,加入到空闲线程中,然后整个 G 就会被放入到全局队列中。

       关于 G,P,M 的个数问题,G 的个数理论上是无限制的,但是受内存限制,P 的数量一般建议是逻辑 CPU 数量的 2 倍,M 的数据默认启动的时候是 10000,内核很难支持这么多线程数,所以整个限制客户忽略,M 一般不做设置,设置好 P,M 一般都是要大于 P。

2、golang的内存管理

         golang的内存管理蛮有意思的,把对象放在一个个的span中,不同大小的对象放在不同的span中,有64种。Go语言内存划分的三个区域分别为:arena区,bitmap区,spans区。对象分配在arena区,基本存储单元为span。bitmap区记录了GC和对象是否包含指针的标志位,spans记录了span索引指针,便于查找。

3、 golang中的make和new关键字

 new 对指针类型分配内存,返回值是分配类型的指针,new也可以对 slice 、map、channel 分配内存;

 make 仅用于 slice、map和 channel 的初始化,返回值为类型本身,而不是指针;new(T)为每个类型分配一片内存,目前看来new(T)并不常用。

4、 golang中的测试方法

golang的测试方法包含:

单元测试、性能测试,都是在testing这个包下面。单元测试是testing.T 和性能测试 testing.B

测试有几个约定需要遵守:

1.一般测试func TestXxx(*testing.T)

测试行必须Test开头,Xxx为字符串,第一个X必须大写的[A-Z]的字母,一般Xxx为被测试方法的函数名。

2.性能测试func BenchmarkXxx(*testing.B)

性能测试用Benchmark标记,Xxx同上。

3. 测试文件名约定

go语言测试文件名约定规则是必须以_test.go结尾,放在相同包下。

举个栗子:这是一个主函数,定义了加法Sum运算。

package main
import “testing”
func TestSum(t *testing.T) {
if sum(1,2) != 3 {
t.Error(“test foo:Sum failed”)
} else {
t.Log(“test foo:Sum pass”)
}
}
运行的话go test就行了。

性能测试代码

package main
import “testing”
func BenchmarkSum(b *testing.B) {
for i := 0; i < b.N; i++ {
sum(1, 2)
}
}

5、 golang中的channel的数据结构

     channel 的数据结构包含 qccount 当前队列中剩余元素个数,dataqsiz 环形队列长度,即可以存放的元素个数,buf 环形队列指针,elemsize 每个元素的大小,closed 标识关闭状态,elemtype 元素类型,sendx 队列下表,指示元素写入时存放到队列中的位置,recv 队列下表,指示元素从队列的该位置读出。recvq 等待读消息的 goroutine 队列,sendq 等待写消息的 goroutine 队列,lock 互斥锁,chan 不允许并发读写。       

      channel 的几个特点 1)、读写值 nil 管道会永久阻塞 2)、关闭的管道读数据仍然可以读数据 3)、往关闭的管道写数据会 panic 4)、关闭为 nil 的管道 panic 5)、关闭已经关闭的管道 panic。

6. golang中select的用法

      golang 中的 select 就是用来监听和 channel 有关的 IO 操作,当 IO 操作发生时,触发相应的动作。有点像系统IO中的select、poll、epoll中的select ,select 只能应用于 channel 的操作,既可以用于 channel 的数据接收,也可以用于 channel 的数据发送。如果 select 的多个分支都满足条件,则会随机的选取其中一个满足条件的分支执行。
select {
    case <- chan1:
        // 如果 chan1 成功读到数据,则进行该 case 处理语句
    case chan2 <- 1:
        // 如果成功向 chan2 写入数据,则进行该 case 处理语句
    default:
        // 如果上面都没有成功,则进入default处理流程
}

7. Golang中的Map与Sync.Map

        原生的go Map在并发读写场景下经常会遇到 panic的情况。造成的原因是map是非线性安全的,并发读写过程中map的数据会被写乱。

        而一般情况下,解决并发读写 map 的思路是加锁,或者把一个 map 切分成若干个小 map,对 key 进行哈希。在业界中使用最多并发指出的模式分别是:  ①原生 map + 互斥锁 或者 读写锁。②  标准库 sync.Map (Go 1.9 及之后)。
        使用的用法如下,声明后调用相应的方法即可:

package main
import (
    "fmt"
    "sync"
)
func main() {
    var m sync.Map
    //Store
    m.Store(1,"a")
    m.Store(2,"b")
    //LoadOrStore
    //若key不存在,则存入key和value,返回false和输入的value
    v,ok := m.LoadOrStore("1","aaa")
    fmt.Println(ok,v) //false aaa
 
    //若key已存在,则返回true和key对应的value,不会修改原来的value
    v,ok = m.LoadOrStore(1,"aaa")
    fmt.Println(ok,v) //false aaa
    //Load
    v,ok = m.Load(1)
    if ok{
        fmt.Println("it's an existing key,value is ",v)
    } else {
        fmt.Println("it's an unknown key")
    }
    //Range
    //遍历sync.Map, 要求输入一个func作为参数
    f := func(k, v interface{}) bool {
        //这个函数的入参、出参的类型都已经固定,不能修改
        //可以在函数体内编写自己的代码,调用map中的k,v
            fmt.Println(k,v)
            return true
        }
    m.Range(f)
    //Delete
    m.Delete(1)
    fmt.Println(m.Load(1))
}

8.Golang的性能调优工具

      首先了解一个概念,profile。

       profile 就是对应用的画像,这里画像就是应用使用 CPU 和内存等情况,也就是说应用使用了多少 CPU 资源、都是哪些部分在使用、每个函数使用的比例是多少、有哪些函数在等待 CPU 资源等等。知道了这些,我们就能对应用进行规划,也能快速定位性能瓶颈。Golang 是一个对性能特别看重的语言,因此语言中自带了 profile 的库。

      golang在语言层面集成了profile采样工具,在程序运行过程中可以获取cpu、heap、block、traces等执行信息,这些会涉及到runtime/pprof、net/http/pprof、runtime/trace等package。

      一般情况下,获取profile数据最有两种形式:web形式与profile文件生成形式。

      具体用法就是引入包 net/http/pprof,然后在代码中

package main
import (
"log"
"net/http"
_ "net/http/pprof"
)
func main() {
// 性能分析
go func() {
log.Println(http.ListenAndServe(":8080", nil))
}()
// 实际业务代码
for {
Sum("test")
}
}
func Sum(str string) string {
data := []byte(str)
sData := string(data)
    var sum = 0
for i := 0; i < 10000; i++ {
    sum += i
}
return sData
}

其中,net/http/pprof包的init函数中注册了以下路由到http服务中,用浏览器打开localhost:8080/debug/pprof/,即可使用pprof提供的功能。

 

文章来自个人专栏
文章 | 订阅
0条评论
0 / 1000
请输入你的评论
0
0