Golang學習篇 協程池

2021-10-06 17:55:33 字數 3516 閱讀 4859

目錄

1. 為什麼需要協程池?

2. 簡單的協程池

3. go-playground/pool

4. ants(推薦)

雖然go語言自帶「高併發」的標籤,其併發程式設計就是由groutine實現的,因其消耗資源低(大約2kb左右,執行緒通常2m左右),效能高效,開發成本低的特性而被廣泛應用到各種場景,例如服務端開發中使用的http服務,在golang net/http包中,每乙個被監聽到的tcp鏈結都是由乙個groutine去完成處理其上下文的,由此使得其擁有極其優秀的併發量吞吐量。

但是,如果無休止的開闢goroutine依然會出現高頻率的排程groutine,那麼依然會浪費很多上下文切換的資源,導致做無用功。所以設計乙個goroutine池限制goroutine的開闢個數在大型併發場景還是必要的。

package main

import (

"fmt"

"time"

)/* 有關task任務相關定義及操作 */

//定義任務task型別,每乙個任務task都可以抽象成乙個函式

type task struct

//通過newtask來建立乙個task

func newtask(f func() error) *task

return &t

}//執行task任務的方法

func (t *task) execute()

/* 有關協程池的定義及操作 */

//定義池型別

type pool struct

//建立乙個協程池

func newpool(cap int) *pool

return &p

}//協程池建立乙個worker並且開始工作

func (p *pool) worker(work_id int)

}//讓協程池pool開始工作

func (p *pool) run()

//2, 從entrychannel協程池入口取外界傳遞過來的任務

// 並且將任務送進jobschannel中

for task := range p.entrychannel

//3, 執行完畢需要關閉jobschannel

close(p.jobschannel)

fmt.println("執行完畢需要關閉jobschannel")

//4, 執行完畢需要關閉entrychannel

close(p.entrychannel)

fmt.println("執行完畢需要關閉entrychannel")

}//主函式

func main() )

//建立乙個協程池,最大開啟3個協程worker

p := newpool(3)

//開乙個協程 不斷的向 pool 輸送列印一條時間的task任務

go func()

}()//啟動協程池p

p.run()

}

上面的協程池雖然簡單,但是對於每乙個併發任務的狀態,pool的狀態缺少控制,我們可以看看go-playground/pool的原始碼實現,「原始碼面前,如同裸奔」。先從每乙個需要執行的任務入手,該庫中對併發單元做了如下的結構體,可以看到除工作單元的值,錯誤,執行函式等,還用了三個分別表示,取消,取消中,寫 的三個併發安全的原子操作值來標識其執行狀態。

github原始碼參考

package main

import (

"fmt"

"gopkg.in/go-playground/pool.v3"

"time"

)func sendmail(int int) pool.workfunc , error)

if wu.iscancelled()

fmt.println("send to", int)

return true, nil

} return fn

}func main()

// 通知批量任務不再接受新的workfunc, 如果新增完workfunc不執行改方法的話將導致取結果集時done channel一直阻塞

batch.queuecomplete()

}()// // 獲取批量任務結果集, 因為 batch.results 中要close results channel 所以不能將其放在loop中執行

r := batch.results()

loop:

for

fmt.println("錯誤結果集:", email.value())

} else

} }}

go-playground/pool相比簡單的協程池, 對pool, worker的狀態有了很好的管理。但是在第乙個實現的簡單groutine池和go-playground/pool中,都是先啟動預定好的groutine來完成任務執行,在併發量遠小於任務量的情況下確實能夠做到groutine的復用,如果任務量不多則會導致任務分配到每個groutine不均勻,甚至可能出現啟動的groutine根本不會執行任務從而導致浪費,而且對於協程池也沒有動態的擴容和縮小。接下來了解下ants的設計和實現。

ants是乙個受fasthttp啟發的高效能協程池,fasthttp號稱是比go原生的net/http快10倍,其原因之一就是採用了各種池化技術, ants相比之前兩種協程池,其模型更像是之前接觸到的資料庫連線池,需要從空餘的worker中取出乙個來執行任務, 當無可用空餘worker的時候再去建立,而當pool的容量達到上線之後,剩餘的任務阻塞等待當前進行中的worker執行完畢將worker放回pool, 直至pool中有空閒worker。 ants在記憶體的管理上做得很好,除了定期清除過期worker(一定時間內沒有分配到任務的worker),ants還實現了一種適用於大批量相同任務的pool, 這種pool與乙個需要大批量重複執行的函式鎖繫結,避免了呼叫方不停的建立,更加節省記憶體。

ants原始碼參考

)//任務

func sendmail(i int, wg *sync.waitgroup) func()

} wg.done() }}

func main()

//申請乙個協程池物件

pool, _ := ants.newpool(2)

//關閉協程池

defer pool.release()

// 向pool提交任務

for i := 1; i <= 5; i++

wg.wait()

}原始碼中提到, ants的吞吐量能夠比原生groutine高出n倍,記憶體節省10到20倍。

原始碼參考:

ants原始碼

參考鏈結

Golang的協程池設計

同一時刻可以處理多個事務 更加節省時間,效率更高 具有並行處理能力的程式我們稱之為 併發程式 併發程式的處理能力優勢體現在 gopool3.jpeg package main import fmt import time func go worker name string fmt.println ...

菜鳥系列Golang學習 協程

有興趣的關注it程式設計師客棧哦 核心態 cpu可以訪問記憶體的所有資料,包括外圍裝置,例如硬碟,網絡卡,cpu也可以將自己從乙個程式切換到另乙個程式。使用者態 只能受限的訪問記憶體,且不允許訪問外圍裝置,占用cpu的能力被剝奪,cpu資源可以被其他程式獲取。由於需要限制不同的程式之間的訪問能力,防...

Golang協程排程

有時候可能會出現這種情況,乙個無恥的goroutine阻止其他goroutine執行。當你有乙個不讓排程器執行的 for迴圈時,這就會發生。package main import fmt func main for done fmt.println done for迴圈並不需要是空的。只要它包含了不...