Go語言sync.Pool(執行緒池)使用
阿新 • • 發佈:2018-12-16
前言
Go 1.3 的sync包中加入一個新特性:Pool。
這個類設計的目的是用來儲存和複用臨時物件,以減少記憶體分配,降低CG壓力。
1 2 3 4 |
|
1、基本使用:
(1)簡單的資料儲存。如果沒有資料,返回執行緒池指定的資料。
package main import ( "fmt" "sync" ) func main() { p:=&sync.Pool{ New: func() interface{}{ return 0 }, } p.Put("jiangzhou") p.Put(123456) fmt.Println(p.Get()) fmt.Println(p.Get()) fmt.Println(p.Get()) }
2、處理臨時變數
垃圾回收一直是Go語言的一塊心病,在它執行垃圾回收的時間中,你很難做什麼。
在垃圾回收壓力大的服務中,GC佔據的CPU有可能超過2%,造成的Pause經常超過2ms。垃圾嚴重的時候,秒級的GC也出現過。
如果經常臨時使用一些大型結構體,可以用Pool來減少GC。
package main import ( "sync" "time" "fmt" ) type structR6 struct { B1 [100000]int } var r6Pool = sync.Pool{ New: func() interface{} { return new(structR6) }, } func usePool() { startTime := time.Now() for i := 0; i < 10000; i++ { sr6 := r6Pool.Get().(*structR6) sr6.B1[0] = 0 r6Pool.Put(sr6) } fmt.Println("pool Used:", time.Since(startTime)) } func standard() { startTime := time.Now() for i := 0; i < 10000; i++ { var sr6 structR6 sr6.B1[0] = 0 } fmt.Println("standard Used:", time.Since(startTime)) } func main() { standard() usePool() }
一個含有100000個int值的結構體,在標準方法中,每次均新建,重複10000次,一共需要耗費188.4969ms;
如果用完的struct可以廢物利用,放回pool中。需要新的結構體的時候,嘗試去pool中取,而不是重新生成,重複10000次僅需要997.8us。
這樣簡單的操作,卻節約了99.65%的時間,也節約了各方面的資源。最重要的是它可以有效減少GC CPU和GC Pause。
(2)用途
-
sync.Pool是一個可以存或取的臨時物件集合
-
sync.Pool可以安全被多個執行緒同時使用,保證執行緒安全
-
注意、注意、注意,sync.Pool中儲存的任何項都可能隨時不做通知的釋放掉,所以不適合用於像socket長連線或資料庫連線池。
-
sync.Pool主要用途是增加臨時物件的重用率,減少GC負擔。
func main() {
//我們建立一個Pool,並實現New()函式
sp := sync.Pool{
//New()函式的作用是當我們從Pool中Get()物件時,如果Pool為空,則先通過New建立一個物件,插入Pool中,然後返回物件。
New: func() interface{} {
return make([]int, 16)
},
}
item := sp.Get()
//列印可以看到,我們通過New返回的大小為16的[]int
fmt.Println("item : ", item)
//然後我們對item進行操作
//New()返回的是interface{},我們需要通過型別斷言來轉換
for i := 0; i < len(item.([]int)); i++ {
item.([]int)[i] = i
}
fmt.Println("item : ", item)
//使用完後,我們把item放回池中,讓物件可以重用
sp.Put(item)
//再次從池中獲取物件
item2 := sp.Get()
//注意這裡獲取的物件就是上面我們放回池中的物件
fmt.Println("item2 : ", item2)
//我們再次獲取物件
item3 := sp.Get()
//因為池中的物件已經沒有了,所以又重新通過New()建立一個新物件,放入池中,然後返回
//所以item3是大小為16的空[]int
fmt.Println("item3 : ", item3)
//測試sync.Pool儲存socket長連線池
//testTcpConnPool()
}
注意、注意、注意,sync.Pool中儲存的任何項都可能隨時不做通知的釋放掉,所以不適合用於像socket長連線或資料庫連線池,如下圖所示。
package main
import (
"sync"
"net"
"fmt"
"runtime"
)
func main() {
sp2 := sync.Pool{
New: func() interface{} {
conn, err := net.Dial("tcp", "127.0.0.1:8888");
if err != nil {
return nil
}
return conn
},
}
buf := make([]byte, 1024)
//獲取物件
conn := sp2.Get().(net.Conn)
//使用物件
conn.Write([]byte("GET / HTTP/1.1 \r\n\r\n"))
n, _ := conn.Read(buf)
fmt.Println("conn read : ", string(buf[:n]))
//列印conn的地址
fmt.Println("coon地址:",conn)
//把物件放回池中
sp2.Put(conn)
//我們人為的進行一次垃圾回收
runtime.GC()
//再次獲取池中的物件
conn2 := sp2.Get().(net.Conn)
//這時發現conn2的地址與上面的conn的地址不一樣了
//說明池中我們之前放回的物件被全部清除了,顯然這並不是我們想看到的
//所以sync.Pool不適合用於scoket長連線或資料庫連線池
fmt.Println("coon2地址",conn2)
}