針對這個資源池管理的一步步都實現(xiàn)了,而且做了詳細的講解,下面就看下整個示例代碼,方便理解。
瑞金ssl適用于網(wǎng)站、小程序/APP、API接口等需要進行數(shù)據(jù)傳輸應用場景,ssl證書未來市場廣闊!成為創(chuàng)新互聯(lián)的ssl證書銷售渠道,可以享受市場價格4-6折優(yōu)惠!如果有意向歡迎電話聯(lián)系或者加微信:028-86922220(備注:SSL證書合作)期待與您的合作!
package commonimport ( "errors" "io" "sync" "log")//一個安全的資源池,被管理的資源必須都實現(xiàn)io.Close接口type Pool struct { m sync.Mutex res chan io.Closer factory func() (io.Closer, error) closed bool}var ErrPoolClosed = errors.New("資源池已經(jīng)被關閉。")//創(chuàng)建一個資源池func New(fn func() (io.Closer, error), size uint) (*Pool, error) { if size <= 0 { return nil, errors.New("size的值太小了。") } return &Pool{ factory: fn, res: make(chan io.Closer, size), }, nil}//從資源池里獲取一個資源func (p *Pool) Acquire() (io.Closer,error) { select { case r,ok := <-p.res: log.Println("Acquire:共享資源") if !ok { return nil,ErrPoolClosed } return r,nil default: log.Println("Acquire:新生成資源") return p.factory() }} //關閉資源池,釋放資源func (p *Pool) Close() { p.m.Lock() defer p.m.Unlock() if p.closed { return } p.closed = true //關閉通道,不讓寫入了 close(p.res) //關閉通道里的資源 for r:=range p.res { r.Close() }}func (p *Pool) Release(r io.Closer){ //保證該操作和Close方法的操作是安全的 p.m.Lock() defer p.m.Unlock() //資源池都關閉了,就省這一個沒有釋放的資源了,釋放即可 if p.closed { r.Close() return } select { case p.res <- r: log.Println("資源釋放到池子里了") default: log.Println("資源池滿了,釋放這個資源吧") r.Close() } }
好了,資源池管理寫好了,也知道資源池是如何實現(xiàn)的啦,現(xiàn)在我們看看如何使用這個資源池,模擬一個數(shù)據(jù)庫連接池吧。
package mainimport ( "flysnow.org/hello/common" "io" "log" "math/rand" "sync" "sync/atomic" "time")const ( //模擬的最大goroutine maxGoroutine = 5 //資源池的大小 poolRes = 2)func main() { //等待任務完成 var wg sync.WaitGroup wg.Add(maxGoroutine) p, err := common.New(createConnection, poolRes) if err != nil { log.Println(err) return } //模擬好幾個goroutine同時使用資源池查詢數(shù)據(jù) for query := 0; query < maxGoroutine; query++ { go func(q int) { dbQuery(q, p) wg.Done() }(query) } wg.Wait() log.Println("開始關閉資源池") p.Close()}//模擬數(shù)據(jù)庫查詢func dbQuery(query int, pool *common.Pool) { conn, err := pool.Acquire() if err != nil { log.Println(err) return } defer pool.Release(conn) //模擬查詢 time.Sleep(time.Duration(rand.Intn(1000)) * time.Millisecond) log.Printf("第%d個查詢,使用的是ID為%d的數(shù)據(jù)庫連接", query, conn.(*dbConnection).ID)}//數(shù)據(jù)庫連接type dbConnection struct { ID int32//連接的標志}//實現(xiàn)io.Closer接口func (db *dbConnection) Close() error { log.Println("關閉連接", db.ID) return nil}var idCounter int32//生成數(shù)據(jù)庫連接的方法,以供資源池使用func createConnection() (io.Closer, error) { //并發(fā)安全,給數(shù)據(jù)庫連接生成唯一標志 id := atomic.AddInt32(&idCounter, 1) return &dbConnection{id}, nil }
這時我們測試使用資源池的例子,首先定義了一個結(jié)構(gòu)體dbConnection,它只有一個字段,用來做唯一標記。然后dbConnection實現(xiàn)了io.Closer接口,這樣才可以使用我們的資源池。
createConnection函數(shù)對應的是資源池中的factory字段,用來創(chuàng)建數(shù)據(jù)庫連接dbConnection的,同時為其賦予了一個為止的標志。
接著我們就同時開了 5 個goroutine,模擬并發(fā)的數(shù)據(jù)庫查詢dbQuery,查詢方法里,先從資源池獲取可用的數(shù)據(jù)庫連接,用完后再釋放。
這里我們會創(chuàng)建 5 個數(shù)據(jù)庫連接,但是我們設置的資源池大小只有 2 ,所以再釋放了 2 個連接后,后面的 3 個連接會因為資源池滿了而釋放不了,一會我們看下輸出的打印信息就可以看到。
最后這個資源連接池使用完之后,我們要關閉資源池,使用資源池的Close方法即可。
2017/04/1722:25:08Acquire:新生成資源
2017/04/1722:25:08Acquire:新生成資源
2017/04/1722:25:08Acquire:新生成資源
2017/04/1722:25:08Acquire:新生成資源
2017/04/1722:25:08Acquire:新生成資源
2017/04/1722:25:08第2個查詢,使用的是ID為4的數(shù)據(jù)庫連接
2017/04/1722:25:08資源釋放到池子里了
2017/04/1722:25:08第4個查詢,使用的是ID為1的數(shù)據(jù)庫連接
2017/04/1722:25:08資源釋放到池子里了
2017/04/1722:25:08第3個查詢,使用的是ID為5的數(shù)據(jù)庫連接
2017/04/1722:25:08資源池滿了,釋放這個資源吧
2017/04/1722:25:08關閉連接5
2017/04/1722:25:09第1個查詢,使用的是ID為3的數(shù)據(jù)庫連接
2017/04/1722:25:09資源池滿了,釋放這個資源吧
2017/04/1722:25:09關閉連接3
2017/04/1722:25:09第0個查詢,使用的是ID為2的數(shù)據(jù)庫連接
2017/04/1722:25:09資源池滿了,釋放這個資源吧
2017/04/1722:25:09關閉連接2
2017/04/1722:25:09開始關閉資源池
2017/04/1722:25:09關閉連接4
2017/04/1722:25:09關閉連接1
到這里,我們已經(jīng)完成了一個資源池的管理,并且進行了使用測試。
資源對象池的使用比較頻繁,因為我們想把一些對象緩存起來,以便使用,這樣就會比較高效,而且不會經(jīng)常調(diào)用GC,為此Go為我們提供了原生的資源池管理,防止我們重復造輪子,這就是sync.Pool,我們看下剛剛我們的例子,如果用sync.Pool實現(xiàn)。
package mainimport(
"log"
"math/rand"
"sync"
"sync/atomic"
"time")const(
//模擬的最大goroutine
maxGoroutine=5)func main(){
//等待任務完成
var wg sync.WaitGroup
wg.Add(maxGoroutine)
p:=&sync.Pool{
New:createConnection,
}
//模擬好幾個goroutine同時使用資源池查詢數(shù)據(jù)
for query:=0; query< maxGoroutine; query++{
go func(qint){
dbQuery(q, p)
wg.Done()
}(query)
}
wg.Wait()}//模擬數(shù)據(jù)庫查詢
func dbQuery(queryint, pool*sync.Pool){
conn:=pool.Get().(*dbConnection)
defer pool.Put(conn)
//模擬查詢
time.Sleep(time.Duration(rand.Intn(1000))* time.Millisecond)
log.Printf("第%d個查詢,使用的是ID為%d的數(shù)據(jù)庫連接", query, conn.ID)}//數(shù)據(jù)庫連接
type dbConnectionstruct{
ID int32//連接的標志}//實現(xiàn)io.Closer接口
func(db*dbConnection)Close() error{
log.Println("關閉連接", db.ID)
returnnil}var idCounter int32//生成數(shù)據(jù)庫連接的方法,以供資源池使用
func createConnection()interface{}{
//并發(fā)安全,給數(shù)據(jù)庫連接生成唯一標志
id:= atomic.AddInt32(&idCounter,1)
return&dbConnection{ID:id}
}
進行微小的改變即可,因為系統(tǒng)庫沒有提供New這類的工廠函數(shù),所以我們使用字面量創(chuàng)建了一個sync.Pool,注意里面的New字段,這是一個返回任意對象的方法,類似我們自己實現(xiàn)的資源池中的factory字段,意思都是一樣的,都是當沒有可用資源的時候,生成一個。
這里我們留意到系統(tǒng)的資源池是沒有大小限制的,也就是說默認情況下是無上限的,受內(nèi)存大小限制。
資源的獲取和釋放對應的方法是Get和Put,也很簡潔,返回任意對象interface{}。
2017/04/1722:42:43第0個查詢,使用的是ID為2的數(shù)據(jù)庫連接
2017/04/1722:42:43第2個查詢,使用的是ID為5的數(shù)據(jù)庫連接
2017/04/1722:42:43第4個查詢,使用的是ID為1的數(shù)據(jù)庫連接
2017/04/1722:42:44第3個查詢,使用的是ID為4的數(shù)據(jù)庫連接
2017/04/1722:42:44第1個查詢,使用的是ID為3的數(shù)據(jù)庫連接
關于系統(tǒng)的資源池,我們需要注意的是它緩存的對象都是臨時的,也就說下一次GC的時候,這些存放的對象都會被清除掉。
新聞名稱:Go語言之并發(fā)示例-Pool(二)
轉(zhuǎn)載來于:http://bm7419.com/article30/iidcso.html
成都網(wǎng)站建設公司_創(chuàng)新互聯(lián),為您提供品牌網(wǎng)站建設、品牌網(wǎng)站設計、服務器托管、網(wǎng)站排名、定制網(wǎng)站、品牌網(wǎng)站制作
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)