runtime 包 提供了運(yùn)行時(shí)與系統(tǒng)的交互,比如控制協(xié)程函數(shù),觸發(fā)垃圾立即回收等等底層操作,下面我們就運(yùn)行時(shí)能做的所有事情逐個(gè)進(jìn)行說(shuō)明與代碼演示
- 1.獲取GOROOT環(huán)境變量
- 2.獲取GO的版本號(hào)
- 3.獲取本機(jī)CPU個(gè)數(shù)
- 4.設(shè)置最大可同時(shí)執(zhí)行的最大CPU數(shù)
- 5.設(shè)置cup profile 記錄的速錄
- 6.查看cup profile 下一次堆棧跟蹤數(shù)據(jù)
- 7.立即執(zhí)行一次垃圾回收
- 8.給變量綁定方法,當(dāng)垃圾回收的時(shí)候進(jìn)行監(jiān)聽(tīng)
- 9.查看內(nèi)存申請(qǐng)和分配統(tǒng)計(jì)信息
- 10.查看程序正在使用的字節(jié)數(shù)
- 11.查看程序正在使用的對(duì)象數(shù)
- 12.獲取調(diào)用堆棧列表
- 13.獲取內(nèi)存profile記錄歷史
- 14.執(zhí)行一個(gè)斷點(diǎn)
- 15.獲取程序調(diào)用go協(xié)程的棧蹤跡歷史
- 16.獲取當(dāng)前函數(shù)或者上層函數(shù)的標(biāo)識(shí)號(hào)、文件名、調(diào)用方法在當(dāng)前文件中的行號(hào)
- 17.獲取與當(dāng)前堆棧記錄相關(guān)鏈的調(diào)用棧蹤跡
- 18.獲取一個(gè)標(biāo)識(shí)調(diào)用棧標(biāo)識(shí)符pc對(duì)應(yīng)的調(diào)用棧
- 19.獲取調(diào)用棧所調(diào)用的函數(shù)的名字
- 20.獲取調(diào)用棧所調(diào)用的函數(shù)的所在的源文件名和行號(hào)
- 21.獲取該調(diào)用棧的調(diào)用棧標(biāo)識(shí)符
- 22.獲取當(dāng)前進(jìn)程執(zhí)行的cgo調(diào)用次數(shù)
- 23.獲取當(dāng)前存在的go協(xié)程數(shù)
- 24.終止掉當(dāng)前的go協(xié)程
- 25.讓其他go協(xié)程優(yōu)先執(zhí)行,等其他協(xié)程執(zhí)行完后,在執(zhí)行當(dāng)前的協(xié)程
- 26.獲取活躍的go協(xié)程的堆棧profile以及記錄個(gè)數(shù)
- 27.將調(diào)用的go協(xié)程綁定到當(dāng)前所在的操作系統(tǒng)線程,其它go協(xié)程不能進(jìn)入該線程
- 28.解除go協(xié)程與操作系統(tǒng)線程的綁定關(guān)系
- 29.獲取線程創(chuàng)建profile中的記錄個(gè)數(shù)
- 30.控制阻塞profile記錄go協(xié)程阻塞事件的采樣率
- 31.返回當(dāng)前阻塞profile中的記錄個(gè)數(shù)
1.獲取GOROOT環(huán)境變量
func GOROOT() string
GOROOT返回Go的根目錄。如果存在GOROOT環(huán)境變量,返回該變量的值;否則,返回創(chuàng)建Go時(shí)的根目錄
package main
import (
"fmt"
"runtime"
)
func main() {
fmt.Println(runtime.GOROOT())
}
image.png
2.獲取GO的版本號(hào)
func Version() string
返回Go的版本字符串。它要么是遞交的hash和創(chuàng)建時(shí)的日期;要么是發(fā)行標(biāo)簽如"go1.3"
package main
import (
"fmt"
"runtime"
)
func main() {
fmt.Println(runtime.Version())
}
image.png
3.獲取本機(jī)CPU個(gè)數(shù)
func NumCPU() int
NumCPU返回本地機(jī)器的邏輯CPU個(gè)數(shù)
package main
import (
"fmt"
"runtime"
)
func main() {
fmt.Println(runtime.NumCPU())
}
image.png
4.設(shè)置最大可同時(shí)執(zhí)行的最大CPU數(shù)
func GOMAXPROCS(n int) int
GOMAXPROCS設(shè)置可同時(shí)執(zhí)行的最大CPU數(shù),并返回先前的設(shè)置。 若 n < 1,它就不會(huì)更改當(dāng)前設(shè)置。本地機(jī)器的邏輯CPU數(shù)可通過(guò) NumCPU 查詢。本函數(shù)在調(diào)度程序優(yōu)化后會(huì)去掉
使用默認(rèn)的cup數(shù)量 我的電腦是4核的
package main
import (
"fmt"
"time"
)
func main() {
//runtime.GOMAXPROCS(1)
startTime := time.Now()
var s1 chan int64 = make(chan int64)
var s2 chan int64 = make(chan int64)
var s3 chan int64 = make(chan int64)
var s4 chan int64 = make(chan int64)
go calc(s1)
go calc(s2)
go calc(s3)
go calc(s4)
<-s1
<-s2
<-s3
<-s4
endTime := time.Now()
fmt.Println(endTime.Sub(startTime))
}
func calc(s chan int64) {
var count int64 = 0
for i := 0 ;i < 1000000000;i++ {
count += int64(i)
}
s <- count
}
下面我們將cup數(shù)量設(shè)置成1
package main
import (
"fmt"
"time"
"runtime"
)
func main() {
runtime.GOMAXPROCS(1)
startTime := time.Now()
var s1 chan int64 = make(chan int64)
var s2 chan int64 = make(chan int64)
var s3 chan int64 = make(chan int64)
var s4 chan int64 = make(chan int64)
go calc(s1)
go calc(s2)
go calc(s3)
go calc(s4)
<-s1
<-s2
<-s3
<-s4
endTime := time.Now()
fmt.Println(endTime.Sub(startTime))
}
func calc(s chan int64) {
var count int64 = 0
for i := 0 ;i < 1000000000;i++ {
count += int64(i)
}
s <- count
}
很明顯速度慢了很多
5.設(shè)置cup profile 記錄的速錄
func SetCPUProfileRate(hz int)
SetCPUProfileRate設(shè)置CPU profile記錄的速率為平均每秒hz次。如果hz<=0,SetCPUProfileRate會(huì)關(guān)閉profile的記錄。如果記錄器在執(zhí)行,該速率必須在關(guān)閉之后才能修改。
絕大多數(shù)使用者應(yīng)使用runtime/pprof包或testing包的-test.cpuprofile選項(xiàng)而非直接使用SetCPUProfileRate
6.查看cup profile 下一次堆棧跟蹤數(shù)據(jù)
func CPUProfile() []byte
目前已廢棄
7.立即執(zhí)行一次垃圾回收
func GC()
GC執(zhí)行一次垃圾回收
看一下代碼
package main
import (
"runtime"
"time"
)
type Student struct {
name string
}
func main() {
var i *Student = new(Student)
runtime.SetFinalizer(i, func(i interface{}) {
println("垃圾回收了")
})
runtime.GC()
time.Sleep(time.Second)
}
我們創(chuàng)建了一個(gè)指針類(lèi)型的變量Student 當(dāng)我們調(diào)用runtime.GC的時(shí)候,內(nèi)存立即會(huì)回收,你可以把runtime.GC()
屏蔽掉,程序就不在執(zhí)行了
8.給變量綁定方法,當(dāng)垃圾回收的時(shí)候進(jìn)行監(jiān)聽(tīng)
func SetFinalizer(x, f interface{})
注意x必須是指針類(lèi)型,f 函數(shù)的參數(shù)一定要和x保持一致,或者寫(xiě)interface{},不然程序會(huì)報(bào)錯(cuò)
示例如下
package main
import (
"runtime"
"time"
)
type Student struct {
name string
}
func main() {
var i *Student = new(Student)
runtime.SetFinalizer(i, func(i *Student) {
println("垃圾回收了")
})
runtime.GC()
time.Sleep(time.Second)
}
image.png
9.查看內(nèi)存申請(qǐng)和分配統(tǒng)計(jì)信息
func ReadMemStats(m *MemStats)
我們可以獲得下面的信息
type MemStats struct {
// 一般統(tǒng)計(jì)
Alloc uint64 // 已申請(qǐng)且仍在使用的字節(jié)數(shù)
TotalAlloc uint64 // 已申請(qǐng)的總字節(jié)數(shù)(已釋放的部分也算在內(nèi))
Sys uint64 // 從系統(tǒng)中獲取的字節(jié)數(shù)(下面XxxSys之和)
Lookups uint64 // 指針查找的次數(shù)
Mallocs uint64 // 申請(qǐng)內(nèi)存的次數(shù)
Frees uint64 // 釋放內(nèi)存的次數(shù)
// 主分配堆統(tǒng)計(jì)
HeapAlloc uint64 // 已申請(qǐng)且仍在使用的字節(jié)數(shù)
HeapSys uint64 // 從系統(tǒng)中獲取的字節(jié)數(shù)
HeapIdle uint64 // 閑置span中的字節(jié)數(shù)
HeapInuse uint64 // 非閑置span中的字節(jié)數(shù)
HeapReleased uint64 // 釋放到系統(tǒng)的字節(jié)數(shù)
HeapObjects uint64 // 已分配對(duì)象的總個(gè)數(shù)
// L低層次、大小固定的結(jié)構(gòu)體分配器統(tǒng)計(jì),Inuse為正在使用的字節(jié)數(shù),Sys為從系統(tǒng)獲取的字節(jié)數(shù)
StackInuse uint64 // 引導(dǎo)程序的堆棧
StackSys uint64
MSpanInuse uint64 // mspan結(jié)構(gòu)體
MSpanSys uint64
MCacheInuse uint64 // mcache結(jié)構(gòu)體
MCacheSys uint64
BuckHashSys uint64 // profile桶散列表
GCSys uint64 // GC元數(shù)據(jù)
OtherSys uint64 // 其他系統(tǒng)申請(qǐng)
// 垃圾收集器統(tǒng)計(jì)
NextGC uint64 // 會(huì)在HeapAlloc字段到達(dá)該值(字節(jié)數(shù))時(shí)運(yùn)行下次GC
LastGC uint64 // 上次運(yùn)行的絕對(duì)時(shí)間(納秒)
PauseTotalNs uint64
PauseNs [256]uint64 // 近期GC暫停時(shí)間的循環(huán)緩沖,最近一次在[(NumGC+255)%256]
NumGC uint32
EnableGC bool
DebugGC bool
// 每次申請(qǐng)的字節(jié)數(shù)的統(tǒng)計(jì),61是C代碼中的尺寸分級(jí)數(shù)
BySize [61]struct {
Size uint32
Mallocs uint64
Frees uint64
}
}
package main
import (
"runtime"
"time"
"fmt"
)
type Student struct {
name string
}
func main() {
var list = make([]*Student,0)
for i:=0;i <100000 ;i++ {
var s *Student = new(Student)
list = append(list, s)
}
memStatus := runtime.MemStats{}
runtime.ReadMemStats(&memStatus)
fmt.Printf("申請(qǐng)的內(nèi)存:%d\n",memStatus.Mallocs)
fmt.Printf("釋放的內(nèi)存次數(shù):%d\n",memStatus.Frees)
time.Sleep(time.Second)
}
10.查看程序正在使用的字節(jié)數(shù)
func (r *MemProfileRecord) InUseBytes() int64
InUseBytes返回正在使用的字節(jié)數(shù)(AllocBytes – FreeBytes)
11.查看程序正在使用的對(duì)象數(shù)
func (r *MemProfileRecord) InUseObjects() int64
InUseObjects返回正在使用的對(duì)象數(shù)(AllocObjects - FreeObjects)
12.獲取調(diào)用堆棧列表
func (r *MemProfileRecord) Stack() []uintptr
Stack返回關(guān)聯(lián)至此記錄的調(diào)用棧蹤跡,即r.Stack0的前綴。
13.獲取內(nèi)存profile記錄歷史
func MemProfile(p []MemProfileRecord, inuseZero bool) (n int, ok bool)
MemProfile返回當(dāng)前內(nèi)存profile中的記錄數(shù)n。若len(p)>=n,MemProfile會(huì)將此分析報(bào)告復(fù)制到p中并返回(n, true);如果len(p)<n,MemProfile則不會(huì)更改p,而只返回(n, false)。
如果inuseZero為真,該profile就會(huì)包含無(wú)效分配記錄(其中r.AllocBytes>0,而r.AllocBytes==r.FreeBytes。這些內(nèi)存都是被申請(qǐng)后又釋放回運(yùn)行時(shí)環(huán)境的)。
大多數(shù)調(diào)用者應(yīng)當(dāng)使用runtime/pprof包或testing包的-test.memprofile標(biāo)記,而非直接調(diào)用MemProfile
14.執(zhí)行一個(gè)斷點(diǎn)
func Breakpoint()
runtime.Breakpoint()
15.獲取程序調(diào)用go協(xié)程的棧蹤跡歷史
func Stack(buf []byte, all bool) int
Stack將調(diào)用其的go程的調(diào)用棧蹤跡格式化后寫(xiě)入到buf中并返回寫(xiě)入的字節(jié)數(shù)。若all為true,函數(shù)會(huì)在寫(xiě)入當(dāng)前go程的蹤跡信息后,將其它所有g(shù)o程的調(diào)用棧蹤跡都格式化寫(xiě)入到buf中。
package main
import (
"time"
"runtime"
"fmt"
)
func main() {
go showRecord()
time.Sleep(time.Second)
buf := make([]byte,10000)
runtime.Stack(buf,true)
fmt.Println(string(buf))
}
func showRecord(){
tiker := time.Tick(time.Second)
for t := range tiker {
fmt.Println(t)
}
}
我們?cè)谡{(diào)用Stack
方法后,首先格式化當(dāng)前go協(xié)程的信息,然后把其他正在運(yùn)行的go協(xié)程也格式化后寫(xiě)入buf中
16.獲取當(dāng)前函數(shù)或者上層函數(shù)的標(biāo)識(shí)號(hào)、文件名、調(diào)用方法在當(dāng)前文件中的行號(hào)
func Caller(skip int) (pc uintptr, file string, line int, ok bool)
package main
import (
"runtime"
"fmt"
)
func main() {
pc,file,line,ok := runtime.Caller(0)
fmt.Println(pc)
fmt.Println(file)
fmt.Println(line)
fmt.Println(ok)
}
pc = 17380971 不是main函數(shù)自己的標(biāo)識(shí) runtime.Caller
方法的標(biāo)識(shí),line = 13 標(biāo)識(shí)它在main方法中的第13行被調(diào)用
package main
import (
"runtime"
"fmt"
)
func main() {
pc,_,line,_ := runtime.Caller(1)
fmt.Printf("main函數(shù)的pc:%d\n",pc)
fmt.Printf("main函數(shù)被調(diào)用的行數(shù):%d\n",line)
show()
}
func show(){
pc,_,line,_ := runtime.Caller(1)
fmt.Printf("show函數(shù)的pc:%d\n",pc)
fmt.Printf("show函數(shù)被調(diào)用的行數(shù):%d\n",line)
// 這個(gè)是main函數(shù)的棧
pc,_,line,_ = runtime.Caller(2)
fmt.Printf("show的上層函數(shù)的pc:%d\n",pc)
fmt.Printf("show的上層函數(shù)被調(diào)用的行數(shù):%d\n",line)
pc,_,_,_ = runtime.Caller(3)
fmt.Println(pc)
pc,_,_,_ = runtime.Caller(4)
fmt.Println(pc)
}
通過(guò)上面的例子我演示了如何追蹤一個(gè)方法被調(diào)用的順序,以及所有相關(guān)函數(shù)的信息
17.獲取與當(dāng)前堆棧記錄相關(guān)鏈的調(diào)用棧蹤跡
func Callers(skip int, pc []uintptr) int
函數(shù)把當(dāng)前go程調(diào)用棧上的調(diào)用棧標(biāo)識(shí)符填入切片pc中,返回寫(xiě)入到pc中的項(xiàng)數(shù)。實(shí)參skip為開(kāi)始在pc中記錄之前所要跳過(guò)的棧幀數(shù),0表示Callers自身的調(diào)用棧,1表示Callers所在的調(diào)用棧。返回寫(xiě)入p的項(xiàng)數(shù)
package main
import (
"runtime"
"fmt"
)
func main() {
pcs := make([]uintptr,10)
i := runtime.Callers(1,pcs)
fmt.Println(pcs[:i])
}
我們獲得了三個(gè)pc 其中有一個(gè)是main方法自身的
18.獲取一個(gè)標(biāo)識(shí)調(diào)用棧標(biāo)識(shí)符pc對(duì)應(yīng)的調(diào)用棧
func FuncForPC(pc uintptr) *Func
package main
import (
"runtime"
)
func main() {
pcs := make([]uintptr,10)
i := runtime.Callers(1,pcs)
for _,pc := range pcs[:i]{
println(runtime.FuncForPC(pc))
}
}
我們知道這個(gè)調(diào)用棧有什么用呢?請(qǐng)繼續(xù)下想看
19.獲取調(diào)用棧所調(diào)用的函數(shù)的名字
func (f *Func) Name() string
package main
import (
"runtime"
)
func main() {
pcs := make([]uintptr,10)
i := runtime.Callers(1,pcs)
for _,pc := range pcs[:i]{
funcPC := runtime.FuncForPC(pc)
println(funcPC.Name())
}
}
20.獲取調(diào)用棧所調(diào)用的函數(shù)的所在的源文件名和行號(hào)
func (f *Func) FileLine(pc uintptr) (file string, line int)
package main
import (
"runtime"
)
func main() {
pcs := make([]uintptr,10)
i := runtime.Callers(1,pcs)
for _,pc := range pcs[:i]{
funcPC := runtime.FuncForPC(pc)
file,line := funcPC.FileLine(pc)
println(funcPC.Name(),file,line)
}
}
21.獲取該調(diào)用棧的調(diào)用棧標(biāo)識(shí)符
func (f *Func) Entry() uintptr
package main
import (
"runtime"
)
func main() {
pcs := make([]uintptr,10)
i := runtime.Callers(1,pcs)
for _,pc := range pcs[:i]{
funcPC := runtime.FuncForPC(pc)
println(funcPC.Entry())
}
}
image.png
22.獲取當(dāng)前進(jìn)程執(zhí)行的cgo調(diào)用次數(shù)
func NumCgoCall() int64
獲取當(dāng)前進(jìn)程調(diào)用c方法的次數(shù)
`
package main
import (
"runtime"
)
/*
#include <stdio.h>
*/
import "C"
func main() {
println(runtime.NumCgoCall())
}
注意我們沒(méi)有調(diào)用c的方法為什么是1呢?因?yàn)?code>import c是,會(huì)調(diào)用了c包中的init方法
下面我們看一個(gè)完整例子
import (
"runtime"
)
/*
#include <stdio.h>
// 自定義一個(gè)c語(yǔ)言的方法
static void myPrint(const char* msg) {
printf("myPrint: %s", msg);
}
*/
import "C"
func main() {
// 調(diào)用c方法
C.myPrint(C.CString("Hello,C\n"))
println(runtime.NumCgoCall())
}
23.獲取當(dāng)前存在的go協(xié)程數(shù)
func NumGoroutine() int
package main
import "runtime"
func main() {
go print()
print()
println(runtime.NumGoroutine())
}
func print(){
}
image.png
我們可以看到輸出的是2 表示存在2個(gè)go協(xié)程 一個(gè)是main.go
另外一個(gè)是go print()
24.終止掉當(dāng)前的go協(xié)程
func Goexit()
package main
import (
"runtime"
"fmt"
)
func main() {
print() // 1
fmt.Println("繼續(xù)執(zhí)行")
}
func print(){
fmt.Println("準(zhǔn)備結(jié)束go協(xié)程")
runtime.Goexit()
defer fmt.Println("結(jié)束了")
}
image.png
Goexit
終止調(diào)用它的go協(xié)程,其他協(xié)程不受影響,Goexit
會(huì)在終止該go協(xié)程前執(zhí)行所有的defer函數(shù),前提是defer必須在它前面定義,如果在main go協(xié)程調(diào)用本方法,會(huì)終止該go協(xié)程,但不會(huì)讓main返回,因?yàn)閙ain函數(shù)沒(méi)有返回,程序會(huì)繼續(xù)執(zhí)行其他go協(xié)程,當(dāng)其他go協(xié)程執(zhí)行完畢后,程序就會(huì)崩潰
25.讓其他go協(xié)程優(yōu)先執(zhí)行,等其他協(xié)程執(zhí)行完后,在執(zhí)行當(dāng)前的協(xié)程
func Gosched()
我們先看一個(gè)示例
package main
import (
"fmt"
)
func main() {
go print() // 1
fmt.Println("繼續(xù)執(zhí)行")
}
func print(){
fmt.Println("執(zhí)行打印方法")
}
我們?cè)?處調(diào)用了go print
方法,但是還未執(zhí)行 main函數(shù)就執(zhí)行完畢了,因?yàn)閮蓚€(gè)協(xié)程是并發(fā)的
那么我們應(yīng)該怎么才能讓每個(gè)協(xié)程都能夠執(zhí)行完畢呢?方法有很多種,不過(guò)就針對(duì)這個(gè)知識(shí)點(diǎn),我們就使用 runtime.Gosched()
來(lái)解決
package main
import (
"fmt"
"runtime"
)
func main() {
go print() // 1
runtime.Gosched()
fmt.Println("繼續(xù)執(zhí)行")
}
func print(){
fmt.Println("執(zhí)行打印方法")
}
image.png
26.獲取活躍的go協(xié)程的堆棧profile以及記錄個(gè)數(shù)
func GoroutineProfile(p []StackRecord) (n int, ok bool)
27.將調(diào)用的go協(xié)程綁定到當(dāng)前所在的操作系統(tǒng)線程,其它go協(xié)程不能進(jìn)入該線程
func LockOSThread()
將調(diào)用的go程綁定到它當(dāng)前所在的操作系統(tǒng)線程。除非調(diào)用的go程退出或調(diào)用UnlockOSThread,否則它將總是在該線程中執(zhí)行,而其它go程則不能進(jìn)入該線程
我們看下面一個(gè)例子
package main
import (
"fmt"
"runtime"
"time"
)
func main() {
go calcSum1()
go calcSum2()
time.Sleep(time.Second*100)
}
func calcSum1(){
runtime.LockOSThread()
start := time.Now()
count := 0
for i := 0; i < 10000000000 ; i++ {
count += i
}
end := time.Now()
fmt.Println("calcSum1耗時(shí)")
fmt.Println(end.Sub(start))
defer runtime.UnlockOSThread()
}
func calcSum2(){
start := time.Now()
count := 0
for i := 0; i < 10000000000 ; i++ {
count += i
}
end := time.Now()
fmt.Println("calcSum2耗時(shí)")
fmt.Println(end.Sub(start))
}
測(cè)試速度沒(méi)有多大的差別,如果有需要協(xié)程,但是有一項(xiàng)重要的功能需要占一個(gè)核,就需要
28.解除go協(xié)程與操作系統(tǒng)線程的綁定關(guān)系
func UnlockOSThread()
將調(diào)用的go程解除和它綁定的操作系統(tǒng)線程。若調(diào)用的go程未調(diào)用LockOSThread,UnlockOSThread不做操作
29.獲取線程創(chuàng)建profile中的記錄個(gè)數(shù)
func ThreadCreateProfile(p []StackRecord) (n int, ok bool)
返回線程創(chuàng)建profile中的記錄個(gè)數(shù)。如果len(p)>=n,本函數(shù)就會(huì)將profile中的記錄復(fù)制到p中并返回(n, true)。若len(p)<n,則不會(huì)更改p,而只返回(n, false)。
絕大多數(shù)使用者應(yīng)當(dāng)使用runtime/pprof包,而非直接調(diào)用ThreadCreateProfile。
30.控制阻塞profile記錄go協(xié)程阻塞事件的采樣率
func SetBlockProfileRate(rate int)
SetBlockProfileRate控制阻塞profile記錄go程阻塞事件的采樣頻率。對(duì)于一個(gè)阻塞事件,平均每阻塞rate納秒,阻塞profile記錄器就采集一份樣本。
要在profile中包括每一個(gè)阻塞事件,需傳入rate=1;要完全關(guān)閉阻塞profile的記錄,需傳入rate<=0。
31.返回當(dāng)前阻塞profile中的記錄個(gè)數(shù)
func BlockProfile(p []BlockProfileRecord) (n int, ok bool)
BlockProfile返回當(dāng)前阻塞profile中的記錄個(gè)數(shù)。如果len(p)>=n,本函數(shù)就會(huì)將此profile中的記錄復(fù)制到p中并返回(n, true)。如果len(p)<n,本函數(shù)則不會(huì)修改p,而只返回(n, false)。
絕大多數(shù)使用者應(yīng)當(dāng)使用runtime/pprof包或testing包的-test.blockprofile標(biāo)記, 而非直接調(diào)用 BlockProfile