Go語(yǔ)言之并發(fā)示例-Pool(二)

針對(duì)這個(gè)資源池管理的一步步都實(shí)現(xiàn)了,而且做了詳細(xì)的講解,下面就看下整個(gè)示例代碼,方便理解。

瑞金ssl適用于網(wǎng)站、小程序/APP、API接口等需要進(jìn)行數(shù)據(jù)傳輸應(yīng)用場(chǎng)景,ssl證書(shū)未來(lái)市場(chǎng)廣闊!成為創(chuàng)新互聯(lián)的ssl證書(shū)銷售渠道,可以享受市場(chǎng)價(jià)格4-6折優(yōu)惠!如果有意向歡迎電話聯(lián)系或者加微信:028-86922220(備注:SSL證書(shū)合作)期待與您的合作!

package commonimport (
    "errors"
    "io"
    "sync"
    "log")//一個(gè)安全的資源池,被管理的資源必須都實(shí)現(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)被關(guān)閉。")//創(chuàng)建一個(gè)資源池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}//從資源池里獲取一個(gè)資源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()
    }}
 //關(guān)閉資源池,釋放資源func (p *Pool) Close() {
    p.m.Lock()
    defer p.m.Unlock()    
    if p.closed { 
           return
    }

    p.closed = true

    //關(guān)閉通道,不讓寫(xiě)入了
    close(p.res)    //關(guān)閉通道里的資源
    for r:=range p.res {
        r.Close()
    }}func (p *Pool) Release(r io.Closer){
    //保證該操作和Close方法的操作是安全的
    p.m.Lock()    
    defer p.m.Unlock()   

     //資源池都關(guān)閉了,就省這一個(gè)沒(méi)有釋放的資源了,釋放即可
    if p.closed {
        r.Close()        
        return 
    }    
    select {
    case p.res <- r:
        log.Println("資源釋放到池子里了")    
    default:
        log.Println("資源池滿了,釋放這個(gè)資源吧")
        r.Close()
    }
}


好了,資源池管理寫(xiě)好了,也知道資源池是如何實(shí)現(xiàn)的啦,現(xiàn)在我們看看如何使用這個(gè)資源池,模擬一個(gè)數(shù)據(jù)庫(kù)連接池吧。

package mainimport (
    "flysnow.org/hello/common"
    "io"
    "log"
    "math/rand"
    "sync"
    "sync/atomic"
    "time")const (    
    //模擬的最大goroutine
    maxGoroutine = 5
    //資源池的大小
    poolRes      = 2)func main() {    
    //等待任務(wù)完成
    var wg sync.WaitGroup
    wg.Add(maxGoroutine)

    p, err := common.New(createConnection, poolRes)    

    if err != nil {
        log.Println(err)        
        return
    }    
    //模擬好幾個(gè)goroutine同時(shí)使用資源池查詢數(shù)據(jù)
    for query := 0; query < maxGoroutine; query++ { 
       go func(q int) {
            dbQuery(q, p)
            wg.Done()
        }(query)
    }

    wg.Wait()
    log.Println("開(kāi)始關(guān)閉資源池")
    p.Close()}//模擬數(shù)據(jù)庫(kù)查詢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個(gè)查詢,使用的是ID為%d的數(shù)據(jù)庫(kù)連接", query, conn.(*dbConnection).ID)}//數(shù)據(jù)庫(kù)連接type dbConnection struct {
    ID int32//連接的標(biāo)志}//實(shí)現(xiàn)io.Closer接口func (db *dbConnection) Close() error {
    log.Println("關(guān)閉連接", db.ID)    
    return nil}var idCounter int32//生成數(shù)據(jù)庫(kù)連接的方法,以供資源池使用func createConnection() (io.Closer, error) {   
    //并發(fā)安全,給數(shù)據(jù)庫(kù)連接生成唯一標(biāo)志
    id := atomic.AddInt32(&idCounter, 1)    
    return &dbConnection{id}, nil
}


這時(shí)我們測(cè)試使用資源池的例子,首先定義了一個(gè)結(jié)構(gòu)體dbConnection,它只有一個(gè)字段,用來(lái)做唯一標(biāo)記。然后dbConnection實(shí)現(xiàn)了io.Closer接口,這樣才可以使用我們的資源池。


createConnection函數(shù)對(duì)應(yīng)的是資源池中的factory字段,用來(lái)創(chuàng)建數(shù)據(jù)庫(kù)連接dbConnection的,同時(shí)為其賦予了一個(gè)為止的標(biāo)志。


接著我們就同時(shí)開(kāi)了 5 個(gè)goroutine,模擬并發(fā)的數(shù)據(jù)庫(kù)查詢dbQuery,查詢方法里,先從資源池獲取可用的數(shù)據(jù)庫(kù)連接,用完后再釋放。


這里我們會(huì)創(chuàng)建 5 個(gè)數(shù)據(jù)庫(kù)連接,但是我們?cè)O(shè)置的資源池大小只有 2 ,所以再釋放了 2 個(gè)連接后,后面的 3 個(gè)連接會(huì)因?yàn)橘Y源池滿了而釋放不了,一會(huì)我們看下輸出的打印信息就可以看到。


最后這個(gè)資源連接池使用完之后,我們要關(guān)閉資源池,使用資源池的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個(gè)查詢,使用的是ID為4的數(shù)據(jù)庫(kù)連接
2017/04/1722:25:08資源釋放到池子里了
2017/04/1722:25:08第4個(gè)查詢,使用的是ID為1的數(shù)據(jù)庫(kù)連接
2017/04/1722:25:08資源釋放到池子里了
2017/04/1722:25:08第3個(gè)查詢,使用的是ID為5的數(shù)據(jù)庫(kù)連接
2017/04/1722:25:08資源池滿了,釋放這個(gè)資源吧
2017/04/1722:25:08關(guān)閉連接5
2017/04/1722:25:09第1個(gè)查詢,使用的是ID為3的數(shù)據(jù)庫(kù)連接
2017/04/1722:25:09資源池滿了,釋放這個(gè)資源吧
2017/04/1722:25:09關(guān)閉連接3
2017/04/1722:25:09第0個(gè)查詢,使用的是ID為2的數(shù)據(jù)庫(kù)連接
2017/04/1722:25:09資源池滿了,釋放這個(gè)資源吧
2017/04/1722:25:09關(guān)閉連接2
2017/04/1722:25:09開(kāi)始關(guān)閉資源池
2017/04/1722:25:09關(guān)閉連接4
2017/04/1722:25:09關(guān)閉連接1


到這里,我們已經(jīng)完成了一個(gè)資源池的管理,并且進(jìn)行了使用測(cè)試。


資源對(duì)象池的使用比較頻繁,因?yàn)槲覀兿氚岩恍?duì)象緩存起來(lái),以便使用,這樣就會(huì)比較高效,而且不會(huì)經(jīng)常調(diào)用GC,為此Go為我們提供了原生的資源池管理,防止我們重復(fù)造輪子,這就是sync.Pool,我們看下剛剛我們的例子,如果用sync.Pool實(shí)現(xiàn)。


package mainimport(
    "log"
    "math/rand"
    "sync"
    "sync/atomic"
    "time")const(    
    //模擬的最大goroutine
    maxGoroutine=5)func main(){
    //等待任務(wù)完成
    var wg sync.WaitGroup
    wg.Add(maxGoroutine)

    p:=&sync.Pool{
        New:createConnection,
    }   
    //模擬好幾個(gè)goroutine同時(shí)使用資源池查詢數(shù)據(jù)
    for query:=0; query< maxGoroutine; query++{
      go func(qint){
            dbQuery(q, p)
            wg.Done()
        }(query)
    }

    wg.Wait()}//模擬數(shù)據(jù)庫(kù)查詢
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個(gè)查詢,使用的是ID為%d的數(shù)據(jù)庫(kù)連接", query, conn.ID)}//數(shù)據(jù)庫(kù)連接
type dbConnectionstruct{    ID int32//連接的標(biāo)志}//實(shí)現(xiàn)io.Closer接口
func(db*dbConnection)Close() error{    log.Println("關(guān)閉連接", db.ID)    returnnil}var idCounter int32//生成數(shù)據(jù)庫(kù)連接的方法,以供資源池使用
func createConnection()interface{}{      //并發(fā)安全,給數(shù)據(jù)庫(kù)連接生成唯一標(biāo)志    id:= atomic.AddInt32(&idCounter,1)    return&dbConnection{ID:id}
}


進(jìn)行微小的改變即可,因?yàn)橄到y(tǒng)庫(kù)沒(méi)有提供New這類的工廠函數(shù),所以我們使用字面量創(chuàng)建了一個(gè)sync.Pool,注意里面的New字段,這是一個(gè)返回任意對(duì)象的方法,類似我們自己實(shí)現(xiàn)的資源池中的factory字段,意思都是一樣的,都是當(dāng)沒(méi)有可用資源的時(shí)候,生成一個(gè)。


這里我們留意到系統(tǒng)的資源池是沒(méi)有大小限制的,也就是說(shuō)默認(rèn)情況下是無(wú)上限的,受內(nèi)存大小限制。


資源的獲取和釋放對(duì)應(yīng)的方法是Get和Put,也很簡(jiǎn)潔,返回任意對(duì)象interface{}。


2017/04/1722:42:43第0個(gè)查詢,使用的是ID為2的數(shù)據(jù)庫(kù)連接
2017/04/1722:42:43第2個(gè)查詢,使用的是ID為5的數(shù)據(jù)庫(kù)連接
2017/04/1722:42:43第4個(gè)查詢,使用的是ID為1的數(shù)據(jù)庫(kù)連接
2017/04/1722:42:44第3個(gè)查詢,使用的是ID為4的數(shù)據(jù)庫(kù)連接
2017/04/1722:42:44第1個(gè)查詢,使用的是ID為3的數(shù)據(jù)庫(kù)連接


關(guān)于系統(tǒng)的資源池,我們需要注意的是它緩存的對(duì)象都是臨時(shí)的,也就說(shuō)下一次GC的時(shí)候,這些存放的對(duì)象都會(huì)被清除掉。

新聞名稱:Go語(yǔ)言之并發(fā)示例-Pool(二)
轉(zhuǎn)載來(lái)于:http://muchs.cn/article30/iidcso.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供品牌網(wǎng)站建設(shè)、品牌網(wǎng)站設(shè)計(jì)、服務(wù)器托管網(wǎng)站排名、定制網(wǎng)站品牌網(wǎng)站制作

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)

成都定制網(wǎng)站網(wǎng)頁(yè)設(shè)計(jì)