自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

為什么Go搞了協(xié)程GoFrame還要搞協(xié)程池?怎么用?什么時候用?

開發(fā) 后端
GoFrame的grpool通過協(xié)程復用,能夠節(jié)省內(nèi)存。結(jié)合我們的需求:如果你的服務器內(nèi)存不高或者業(yè)務場景對內(nèi)存占用的要求更高,那就使用grpool。如果服務器的內(nèi)存足夠,但是對耗時有較高的要求,就用原生的goroutine。

最近收到「程序員升級打怪」知識星球[1]的提問:“go協(xié)程本來就是輕量級線程,還有必要做復用增加工作量嗎,性能可以提升多少呢?”

先說結(jié)論

  • Go的協(xié)程goroutine非常輕量級,這也是Go天生支持高并發(fā)的主要原因。
  • 但是協(xié)程goroutine頻繁的創(chuàng)建銷毀對GC的壓力比較大,會影響性能。
  • grpool的作用就是復用goroutine,減少頻繁創(chuàng)建銷毀的性能損耗。grpool相比于goroutine更節(jié)省內(nèi)存,但是耗時更長;
  • 原因也很簡單:grpool復用了協(xié)程,減少了協(xié)程的創(chuàng)建和銷毀,減少了內(nèi)存消耗;也因為協(xié)程的復用,總的協(xié)程數(shù)量減少,導致耗時變長。(一起干活的同事變少了,項目不就延期了嘛,很好理解。)
  • 所以:GoFrame的grpool通過協(xié)程復用,能夠節(jié)省內(nèi)存。結(jié)合我們的需求:如果你的服務器內(nèi)存不高或者業(yè)務場景對內(nèi)存占用的要求更高,那就使用grpool。如果服務器的內(nèi)存足夠,但是對耗時有較高的要求,就用原生的goroutine。

名詞解釋

Pool: goroutine池,用于管理若干可復用的goroutine協(xié)程資源

Worker: 池對象中參與任務執(zhí)行的goroutine,一個worker可以執(zhí)行若干個job,直到隊列中再無等待的job

Job:添加到池對象的任務隊列中等待執(zhí)行的任務,是一個func()方法,一個job同時只能被一個worker獲取并執(zhí)行。

使用示例

使用默認的協(xié)程池,限制100個協(xié)程執(zhí)行1000個任務

pool.Size() 獲得當前工作的協(xié)程數(shù)量

pool.Jobs() 獲得當前池中待處理的任務數(shù)量

package main

import (
"fmt"
"github.com/gogf/gf/os/grpool"
"github.com/gogf/gf/os/gtimer"
"sync"
"time"
)

func main() {
pool := grpool.New(100)

//添加1千個任務
for i := 0; i < 1000; i++ {
_ = pool.Add(job)
}

fmt.Println("worker:", pool.Size()) //當前工作的協(xié)程數(shù)量
fmt.Println("jobs:", pool.Jobs()) //當前池中待處理的任務數(shù)量

gtimer.SetInterval(time.Second, func() {
fmt.Println("worker:", pool.Size()) //當前工作的協(xié)程數(shù)
fmt.Println("jobs:", pool.Jobs()) //當前池中待處理的任務數(shù)
})

//阻止進程結(jié)束
select {}
}

//任務方法
func job() {
time.Sleep(time.Second)
}

打印結(jié)果

圖片

是不是灰常簡單~

踩坑之旅

一個簡單的場景,請使用協(xié)程打印0~9。

常犯的錯誤

大家看下面的代碼有沒有問題,請預測一下打印結(jié)果。

wg := sync.WaitGroup{}
for i := 0; i < 9; i++ {
wg.Add(1)
go func() {
fmt.Println(i)
wg.Done()
}()
}
wg.Wait()

不用著急看答案

.

.

.

猜一下打印結(jié)果是什么。

打印結(jié)果

圖片

分析原因

對于異步線程/協(xié)程來講,函數(shù)進行異步執(zhí)行注冊時,該函數(shù)并未真正開始執(zhí)行。

(注冊時只在goroutine?的棧中保存了變量i的內(nèi)存地址)

而一旦開始執(zhí)行時函數(shù)才會去讀取變量i?的值,而這個時候變量i?的值已經(jīng)自增到了9。

正確寫法

wg := sync.WaitGroup{}
for i := 0; i < 9; i++ {
wg.Add(1)
go func(v int) {
fmt.Println(v)
wg.Done()
}(i)
}
wg.Wait()

打印結(jié)果

圖片

使用grpool

使用grpool和使用go一樣,都需要把當前變量i的值賦值給一個不會改變的臨時變量,在函數(shù)中使用該臨時變量而不是直接使用變量i。

正確代碼

wg := sync.WaitGroup{}
for i := 0; i < 9; i++ {
wg.Add(1)
v := i //grpool.add() 的參數(shù)只能是不帶參數(shù)的匿名函數(shù) 因此只能以設置臨時變量的方式賦值
_ = grpool.Add(func() {
fmt.Println(v)
wg.Done()
})
}
wg.Wait()

打印結(jié)果

圖片

錯誤代碼

注意:這是錯誤的演示,不要這么寫~

wg := sync.WaitGroup{}
for i := 0; i < 9; i++ {
wg.Add(1)
_ = grpool.Add(func() {
fmt.Println(i) //打印結(jié)果都是9
wg.Done()
})
}
wg.Wait()

打印結(jié)果

圖片

性能測試

使用for循環(huán),開啟一萬個協(xié)程,分別使用原生goroutine和grpool執(zhí)行。

看兩者在內(nèi)存占用和耗時方面的差別。

package main

import (
"flag"
"fmt"
"github.com/gogf/gf/os/grpool"
"github.com/gogf/gf/os/gtime"
"log"
"os"
"runtime"
"runtime/pprof"
"sync"
"time"
)

func main() {
//接收命令行參數(shù)
flag.Parse()
//cpu分析
cpuProfile()
//主邏輯
//demoGrpool()
demoGoroutine()
//內(nèi)存分析
memProfile()
}

func demoGrpool() {
start := gtime.TimestampMilli()
wg := sync.WaitGroup{}
for i := 0; i < 10000; i++ {
wg.Add(1)
_ = grpool.Add(func() {
var m runtime.MemStats
runtime.ReadMemStats(&m)
fmt.Printf("運行中占用內(nèi)存:%d Kb\n", m.Alloc/1024)
time.Sleep(time.Millisecond)
wg.Done()
})
fmt.Printf("運行的協(xié)程:", grpool.Size())
}
wg.Wait()
fmt.Printf("運行的時間:%v ms \n", gtime.TimestampMilli()-start)
select {}
}

func demoGoroutine() {
//start := gtime.TimestampMilli()
wg := sync.WaitGroup{}
for i := 0; i < 10000; i++ {
wg.Add(1)
go func() {
//var m runtime.MemStats
//runtime.ReadMemStats(&m)
//fmt.Printf("運行中占用內(nèi)存:%d Kb\n", m.Alloc/1024)
time.Sleep(time.Millisecond)
wg.Done()
}()
}
wg.Wait()
//fmt.Printf("運行的時間:%v ms \n", gtime.TimestampMilli()-start)
}

var cpuprofile = flag.String("cpuprofile", "", "write cpu profile `file`")
var memprofile = flag.String("memprofile", "", "write memory profile to `file`")

func cpuProfile() {
if *cpuprofile != "" {
f, err := os.Create(*cpuprofile)
if err != nil {
log.Fatal("could not create CPU profile: ", err)
}
if err := pprof.StartCPUProfile(f); err != nil { //監(jiān)控cpu
log.Fatal("could not start CPU profile: ", err)
}
defer pprof.StopCPUProfile()
}
}

func memProfile() {
if *memprofile != "" {
f, err := os.Create(*memprofile)
if err != nil {
log.Fatal("could not create memory profile: ", err)
}
runtime.GC() // GC,獲取最新的數(shù)據(jù)信息
if err := pprof.WriteHeapProfile(f); err != nil { // 寫入內(nèi)存信息
log.Fatal("could not write memory profile: ", err)
}
f.Close()
}
}

運行結(jié)果

組件

占用內(nèi)存

耗時

grpool

2229 Kb

1679 ms

goroutine

5835 Kb

1258 ms

性能測試結(jié)果分析

通過測試結(jié)果我們能很明顯的看出來,在相同的環(huán)境下執(zhí)行相同的任務:

grpool相比于goroutine,內(nèi)存占用更少,耗時更長;

goroutine相比于grpool占用內(nèi)存更高,耗時更短。

總結(jié)

我們再來回顧一下開篇的結(jié)論,相信通過仔細閱讀,你一定有了更好的理解:

  • Go的協(xié)程goroutine非常輕量級,這也是Go天生支持高并發(fā)的主要原因。
  • 但是協(xié)程goroutine頻繁的創(chuàng)建銷毀對GC的壓力比較大,會影響性能。
  • grpool的作用就是復用goroutine,減少頻繁創(chuàng)建銷毀的性能損耗。grpool相比于goroutine更節(jié)省內(nèi)存,但是耗時更長;
  • 原因也很簡單:grpool復用了協(xié)程,減少了協(xié)程的創(chuàng)建和銷毀,減少了內(nèi)存消耗;也因為協(xié)程的復用,總的協(xié)程數(shù)量減少,導致耗時變長。(一起干活的同事變少了,項目不就延期了嘛,很好理解。)
  • 所以:goframe的grpool通過協(xié)程復用,能夠節(jié)省內(nèi)存。結(jié)合我們的需求:如果你的服務器內(nèi)存不高或者業(yè)務場景對內(nèi)存占用的要求更高,那就使用grpool。如果服務器的內(nèi)存足夠,但是對耗時有較高的要求,就用原生的goroutine。
  • 文中的易錯代碼部分可以再重點消化一下。

參考資料

[1]「程序員升級打怪」知識星球: https://wx.zsxq.com/dweb2/index/group/15528828844882

歡迎Star GoFrame:https://github.com/gogf/gf

本文轉(zhuǎn)載自微信公眾號「 程序員升級打怪之旅」,作者「王中陽Go」,可以通過以下二維碼關(guān)注。

轉(zhuǎn)載本文請聯(lián)系「 程序員升級打怪之旅」公眾號。

責任編輯:武曉燕 來源: 程序員升級打怪之旅
相關(guān)推薦

2025-02-28 09:04:08

2021-04-25 09:36:20

Go協(xié)程線程

2016-10-28 17:39:47

phpgolangcoroutine

2021-09-16 09:59:13

PythonJavaScript代碼

2020-06-19 08:01:48

Kotlin 協(xié)程編程

2023-11-26 18:35:25

Python編程語言

2018-12-04 14:00:41

協(xié)程編程模式PHP

2024-12-03 15:15:22

2024-06-27 07:56:49

2023-11-17 11:36:59

協(xié)程纖程操作系統(tǒng)

2023-07-30 23:44:49

Go協(xié)程進程

2023-07-27 13:46:10

go開源項目

2021-05-13 21:58:00

高并發(fā)應用Asyncio

2025-02-08 09:13:40

2021-12-09 06:41:56

Python協(xié)程多并發(fā)

2023-10-24 19:37:34

協(xié)程Java

2023-12-27 08:07:49

Golang協(xié)程池Ants

2024-05-29 08:05:15

Go協(xié)程通信

2021-09-10 17:02:43

Python協(xié)程goroutine

2023-07-13 08:06:05

應用協(xié)程阻塞
點贊
收藏

51CTO技術(shù)棧公眾號