目錄
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迴圈並不需要是空的。只要它包含了不...