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\n", conn.ID)
return nil
}
//给每个连接资源给id
var idCounter int32
//创建新资源
func createConnection() (io.Closer, error) {
id := atomic.AddInt32(&idCounter, 1)
log.Printf("创建新资源,id:%d\n", 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\n", 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