zoukankan      html  css  js  c++  java
  • Go语言-并发模式-资源池实例(pool)

    Go语言并发模式

    利用goroutine和channel进行go的并发模式,实现一个资源池实例(《Go语言实战》书中实例稍作修改)

    资源池可以存储一定数量的资源,用户程序从资源池获取资源进行使用,使用完成将资源释放回资源池

    程序

    pool.go

    package pool
    
    import (
        "errors"
        "io"
        "log"
        "sync"
        "time"
    )
    
    type Pool struct {
        m        sync.Mutex
        resource chan io.Closer
        //创建资源的方法,由用户程序自己生成传入
        factory func() (io.Closer, error)
        closed  bool
        //资源池获取资源超时时间
        timeout <-chan time.Time
    }
    
    //资源池关闭标志
    var ErrPoolClosed = errors.New("资源池已经关闭")
    //超时标志
    var ErrTimeout = errors.New("获取资源超时")
    
    //新建资源池
    func New(fn func() (io.Closer, error), size int) (*Pool, error) {
        if size <= 0 {
            return nil, errors.New("新建资源池大小太小")
        }
        //新建资源池
        p := Pool{
            factory:  fn,
            resource: make(chan io.Closer, size),
        }
        //向资源池循环添加资源,直到池满
        for count := 1; count <= cap(p.resource); count++ {
            r, err := fn()
            if err != nil {
                log.Println("添加资源失败,创建资源方法返回nil")
                break
            }
            log.Println("资源加入资源池")
            p.resource <- r
        }
        log.Println("资源池已满,返回资源池")
        return &p, nil
    }
    
    //获取资源
    func (p *Pool) Acquire(d time.Duration) (io.Closer, error) {
        //设置d时间后超时
        p.timeout = time.After(d)
        select {
        case r, ok := <-p.resource:
            log.Println("获取", "共享资源")
            if !ok {
                return nil, ErrPoolClosed
            }
            return r, nil
        case <-p.timeout:
            return nil, ErrTimeout
        }
    }
    
    //放回资源池
    func (p *Pool) Release(r io.Closer) {
        //上互斥锁,和Close方法对应,不同时操作
        p.m.Lock()
        defer p.m.Unlock()
    
        if p.closed {
            r.Close()
            return
        }
        //资源放回队列
        select {
        case p.resource <- r:
            log.Println("资源放回队列")
        default:
            log.Println("资源队列已满,释放资源")
            r.Close()
        }
    }
    
    //关闭资源池
    func (p *Pool) Close() {
        //互斥锁,保证同步,和Release方法相关,用同一把锁
        p.m.Lock()
        defer p.m.Unlock()
    
        if p.closed {
            return
        }
        p.closed = true
        //清空通道资源之前,将通道关闭,否则引起死锁
        close(p.resource)
        for r := range p.resource {
            r.Close()
        }
    }

    main.go

    package main
    
    import (
        "gopro/patterns/pool"
        "io"
        "log"
        "math/rand"
        "sync"
        "sync/atomic"
        "time"
    )
    
    const (
        maxGoroutines   = 25
        pooledResources = 2
    )
    
    //实现接口类型 资源类型
    type dbConnection struct {
        ID int32
    }
    
    //实现接口方法
    func (conn *dbConnection) Close() error {
        log.Printf("资源关闭,ID:%d
    ", conn.ID)
        return nil
    }
    
    //给每个连接资源给id
    var idCounter int32
    //创建新资源
    func createConnection() (io.Closer, error) {
        id := atomic.AddInt32(&idCounter, 1)
        log.Printf("创建新资源,id:%d
    ", id)
        return &dbConnection{ID: id}, nil
    }
    
    //测试资源池
    func performQueries(query int, p *pool.Pool) {
        conn, err := p.Acquire(10 * time.Second)
        if err != nil {
            log.Println("获取资源超时")
            log.Println(err)
            return
        }
        //方法结束后将资源放进资源池
        defer p.Release(conn)
        //模拟使用资源
        time.Sleep(time.Duration(rand.Intn(1000)) * time.Millisecond)
        log.Printf("查询goroutine id:%d,资源ID:%d
    ", query, conn.(*dbConnection).ID)
    }
    
    func main() {
        var wg sync.WaitGroup
        wg.Add(maxGoroutines)
    
        p, err := pool.New(createConnection, pooledResources)
        if err != nil {
            log.Println(err)
        }
    
        //每个goroutine一个查询,每个查询从资源池中获取资源
        for query := 0; query < maxGoroutines; query++ {
            go func(q int) {
                performQueries(q, p)
                wg.Done()
            }(query)
        }
    
        //主线程等待
        wg.Wait()
        log.Println("程序结束")
        //释放资源
        p.Close()
    }

    执行结果

    循环使用两个资源

    2019/06/21 00:45:34 创建新资源,id:1
    2019/06/21 00:45:34 资源加入资源池
    2019/06/21 00:45:34 创建新资源,id:2
    2019/06/21 00:45:34 资源加入资源池
    2019/06/21 00:45:34 资源池已满,返回资源池
    2019/06/21 00:45:34 获取 共享资源
    2019/06/21 00:45:34 获取 共享资源
    2019/06/21 00:45:34 查询goroutine id:1,资源ID:1
    2019/06/21 00:45:34 资源放回队列
    2019/06/21 00:45:34 获取 共享资源
    2019/06/21 00:45:35 查询goroutine id:0,资源ID:2
    2019/06/21 00:45:35 资源放回队列
    2019/06/21 00:45:35 获取 共享资源
    2019/06/21 00:45:35 查询goroutine id:4,资源ID:1
    2019/06/21 00:45:35 资源放回队列
    2019/06/21 00:45:35 获取 共享资源
    2019/06/21 00:45:35 查询goroutine id:24,资源ID:2
    2019/06/21 00:45:35 资源放回队列
    2019/06/21 00:45:35 获取 共享资源
    2019/06/21 00:45:35 查询goroutine id:3,资源ID:1
    2019/06/21 00:45:35 资源放回队列
    2019/06/21 00:45:35 获取 共享资源
    2019/06/21 00:45:35 查询goroutine id:14,资源ID:2
    2019/06/21 00:45:35 资源放回队列
    2019/06/21 00:45:35 获取 共享资源
    2019/06/21 00:45:35 查询goroutine id:5,资源ID:1
    2019/06/21 00:45:35 资源放回队列
    2019/06/21 00:45:35 获取 共享资源
    2019/06/21 00:45:35 查询goroutine id:6,资源ID:2
    2019/06/21 00:45:35 资源放回队列
    2019/06/21 00:45:35 获取 共享资源
    2019/06/21 00:45:36 查询goroutine id:7,资源ID:1
    2019/06/21 00:45:36 资源放回队列
    2019/06/21 00:45:36 获取 共享资源
    2019/06/21 00:45:36 查询goroutine id:8,资源ID:2
    2019/06/21 00:45:36 资源放回队列
    2019/06/21 00:45:36 获取 共享资源
    2019/06/21 00:45:36 查询goroutine id:9,资源ID:1
    2019/06/21 00:45:36 资源放回队列
    2019/06/21 00:45:36 获取 共享资源
    2019/06/21 00:45:36 查询goroutine id:10,资源ID:2
    2019/06/21 00:45:36 资源放回队列
    2019/06/21 00:45:36 获取 共享资源
    2019/06/21 00:45:36 查询goroutine id:12,资源ID:2
    2019/06/21 00:45:36 资源放回队列
    2019/06/21 00:45:36 获取 共享资源
    2019/06/21 00:45:36 查询goroutine id:11,资源ID:1
    2019/06/21 00:45:36 资源放回队列
    2019/06/21 00:45:36 获取 共享资源
    2019/06/21 00:45:37 查询goroutine id:15,资源ID:1
    2019/06/21 00:45:37 资源放回队列
    2019/06/21 00:45:37 获取 共享资源
    2019/06/21 00:45:37 查询goroutine id:20,资源ID:1
    2019/06/21 00:45:37 资源放回队列
    2019/06/21 00:45:37 获取 共享资源
    2019/06/21 00:45:37 查询goroutine id:13,资源ID:2
    2019/06/21 00:45:37 资源放回队列
    2019/06/21 00:45:37 获取 共享资源
    2019/06/21 00:45:37 查询goroutine id:16,资源ID:2
    2019/06/21 00:45:37 资源放回队列
    2019/06/21 00:45:37 获取 共享资源
    2019/06/21 00:45:37 查询goroutine id:19,资源ID:1
    2019/06/21 00:45:37 资源放回队列
    2019/06/21 00:45:37 获取 共享资源
    2019/06/21 00:45:37 查询goroutine id:21,资源ID:2
    2019/06/21 00:45:37 资源放回队列
    2019/06/21 00:45:37 获取 共享资源
    2019/06/21 00:45:38 查询goroutine id:17,资源ID:1
    2019/06/21 00:45:38 资源放回队列
    2019/06/21 00:45:38 获取 共享资源
    2019/06/21 00:45:38 查询goroutine id:22,资源ID:2
    2019/06/21 00:45:38 资源放回队列
    2019/06/21 00:45:38 获取 共享资源
    2019/06/21 00:45:38 查询goroutine id:23,资源ID:2
    2019/06/21 00:45:38 资源放回队列
    2019/06/21 00:45:38 获取 共享资源
    2019/06/21 00:45:38 查询goroutine id:2,资源ID:2
    2019/06/21 00:45:38 资源放回队列
    2019/06/21 00:45:38 查询goroutine id:18,资源ID:1
    2019/06/21 00:45:38 资源放回队列
    2019/06/21 00:45:38 程序结束
    2019/06/21 00:45:38 资源关闭,ID:2
    2019/06/21 00:45:38 资源关闭,ID:1

    超时结果

    修改超时时间为很短 

    conn, err := p.Acquire(2 * time.Second)

    结果:

    2019/06/21 00:58:33 创建新资源,id:1
    2019/06/21 00:58:33 资源加入资源池
    2019/06/21 00:58:33 创建新资源,id:2
    2019/06/21 00:58:33 资源加入资源池
    2019/06/21 00:58:33 资源池已满,返回资源池
    2019/06/21 00:58:33 获取 共享资源
    2019/06/21 00:58:33 获取 共享资源
    2019/06/21 00:58:33 查询goroutine id:0,资源ID:1
    2019/06/21 00:58:33 资源放回队列
    2019/06/21 00:58:33 获取 共享资源
    2019/06/21 00:58:34 查询goroutine id:5,资源ID:2
    2019/06/21 00:58:34 资源放回队列
    2019/06/21 00:58:34 获取 共享资源
    2019/06/21 00:58:34 查询goroutine id:24,资源ID:1
    2019/06/21 00:58:34 资源放回队列
    2019/06/21 00:58:34 获取 共享资源
    2019/06/21 00:58:34 查询goroutine id:11,资源ID:2
    2019/06/21 00:58:34 资源放回队列
    2019/06/21 00:58:34 获取 共享资源
    2019/06/21 00:58:34 查询goroutine id:1,资源ID:1
    2019/06/21 00:58:34 资源放回队列
    2019/06/21 00:58:34 获取 共享资源
    2019/06/21 00:58:34 查询goroutine id:2,资源ID:2
    2019/06/21 00:58:34 资源放回队列
    2019/06/21 00:58:34 获取 共享资源
    2019/06/21 00:58:34 查询goroutine id:3,资源ID:1
    2019/06/21 00:58:34 资源放回队列
    2019/06/21 00:58:34 获取 共享资源
    2019/06/21 00:58:34 查询goroutine id:4,资源ID:2
    2019/06/21 00:58:34 资源放回队列
    2019/06/21 00:58:34 获取 共享资源
    2019/06/21 00:58:35 查询goroutine id:17,资源ID:1
    2019/06/21 00:58:35 资源放回队列
    2019/06/21 00:58:35 获取 共享资源
    2019/06/21 00:58:35 获取资源超时
    2019/06/21 00:58:35 获取资源超时
    2019/06/21 00:58:35 获取资源超时
    2019/06/21 00:58:35 获取资源超时
    2019/06/21 00:58:35 获取资源超时
    2019/06/21 00:58:35 获取资源超时
    2019/06/21 00:58:35 获取资源超时
    2019/06/21 00:58:35 获取资源超时
    2019/06/21 00:58:35 获取资源超时
    2019/06/21 00:58:35 获取资源超时
    2019/06/21 00:58:35 获取资源超时
    2019/06/21 00:58:35 获取资源超时
    2019/06/21 00:58:35 获取资源超时
    2019/06/21 00:58:35 获取资源超时
    2019/06/21 00:58:35 获取资源超时
    2019/06/21 00:58:35 获取资源超时
    2019/06/21 00:58:35 获取资源超时
    2019/06/21 00:58:35 获取资源超时
    2019/06/21 00:58:35 获取资源超时
    2019/06/21 00:58:35 获取资源超时
    2019/06/21 00:58:35 获取资源超时
    2019/06/21 00:58:35 获取资源超时
    2019/06/21 00:58:35 获取资源超时
    2019/06/21 00:58:35 获取资源超时
    2019/06/21 00:58:35 获取资源超时
    2019/06/21 00:58:35 获取资源超时
    2019/06/21 00:58:35 获取资源超时
    2019/06/21 00:58:35 获取资源超时
    2019/06/21 00:58:35 查询goroutine id:12,资源ID:2
    2019/06/21 00:58:35 资源放回队列
    2019/06/21 00:58:35 查询goroutine id:13,资源ID:1
    2019/06/21 00:58:35 资源放回队列
    2019/06/21 00:58:35 程序结束
    2019/06/21 00:58:35 资源关闭,ID:2
    2019/06/21 00:58:35 资源关闭,ID:1
  • 相关阅读:
    JS运行机制之 Event Loop 的思考
    模块机制 之commonJs、node模块 、AMD、CMD
    git报错:'fatal:remote origin already exists'怎么处理?附上git常用操作以及说明。
    Uncaught RangeError: Maximum call stack size exceeded-栈溢出
    对循环内部反复声明变量的写法的一点想法?
    JS的forEach和map方法的区别
    函数的属性和方法之call、apply 及bind
    利用Apach ab对nodejs进行并发负载的压力测试
    怎么判断一个对象是不是数组类型?
    Python模块学习之fabric
  • 原文地址:https://www.cnblogs.com/limaosheng/p/11062338.html
Copyright © 2011-2022 走看看