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