[Go] golang缓冲通道实现资源池
go的pool资源池:
1.当有多个并发请求的时候,比如需要查询数据库
2.先创建一个2个容量的数据库连接资源池
3.当一个请求过来的时候,去资源池里请求连接资源,肯定是空的就创建一个连接,执行查询,结束后放入了资源池里
4.当第二个请求过来的时候,也是去资源池请求连接资源,就直接在池中拿过来一个连接进行查询
5.当并发大的时候,资源池里面没有足够连接资源,就会不停创建新资源,放入池里面的时候,也会放不进去,就主动关闭掉这个资源
6.这里的资源池实质上是一个缓冲通道,里面放着连接资源
package main import (
"errors"
"io"
"log"
"math/rand"
"sync"
"sync/atomic"
"time"
) //定义一个结构体,这个实体类型可以作为整体单元被复制,可以作为参数或返回值,或被存储到数组
type Pool struct {
//定义成员,互斥锁类型
m sync.Mutex
//定义成员,通道类型,通道传递的是io.Closer类型
resources chan io.Closer
//定义工厂成员,类型是func()(io.Closer,error)
//error是预定义类型,实际上是个interface接口类型
factory func() (io.Closer, error)
closed bool
} //定义变量,函数返回的是error类型
var ErrPoolClosed = errors.New("池已经关闭了") //定义New方法,创建一个池,返回的是Pool类型的指针
//传入的参数是个函数类型func(io.Closer,error)和池的大小
func New(fn func() (io.Closer, error), size uint) (*Pool, error) {
//使用结构体字面值给结构体成员赋值
myPool := Pool{
factory: fn,
resources: make(chan io.Closer, size),
}
//返回两个返回值
return &myPool, nil
} //从池中请求获取一个资源,给Pool类型定义的方法
//返回的值是io.Closer类型
func (p *Pool) Acquire() (io.Closer, error) {
//基于select的多路复用
//select会等待case中有能够执行的,才会去执行,等待其中一个能执行就执行
//default分支会在所有case没法执行时,默认执行,也叫轮询channel
select {
case r, _ := <-p.resources:
log.Printf("请求资源:来自通道 %d", r.(*dbConn).ID)
return r, nil
//如果缓冲通道中没有了,就会执行这里
default:
log.Printf("请求资源:创建新资源")
return p.factory()
}
} //将一个使用后的资源放回池
//传入的参数是io.Closer类型
func (p *Pool) Release(r io.Closer) {
//使用mutex互斥锁
p.m.Lock()
//解锁
defer p.m.Unlock()
//如果池都关闭了
if p.closed {
//关掉资源
r.Close()
return
}
//select多路选择
//如果放回通道的时候满了,就关闭这个资源
select {
case p.resources <- r:
log.Printf("释放资源:放入通道 %d", r.(*dbConn).ID)
default:
log.Printf("释放资源:关闭资源%d", r.(*dbConn).ID)
r.Close()
}
} //关闭资源池,关闭通道,将通道中的资源关掉
func (p *Pool) Close() {
p.m.Lock()
defer p.m.Unlock()
p.closed = true
//先关闭通道再清空资源
close(p.resources)
//清空并关闭资源
for r := range p.resources {
r.Close()
}
} //定义全局常量
const (
maxGoroutines = 20 //使用25个goroutine模拟同时的连接请求
poolSize = 2 //资源池中的大小
) //定义结构体,模拟要共享的资源
type dbConn struct {
//定义成员
ID int32
} //dbConn实现io.Closer接口
func (db *dbConn) Close() error {
return nil
} var idCounter int32 //定义一个全局的共享的变量,更新时用原子函数锁住
//定义方法,创建dbConn实例
//返回的是io.Closer类型和error类型
func createConn() (io.Closer, error) {
//原子函数锁住,更新加1
id := atomic.AddInt32(&idCounter, 1)
log.Printf("创建新资源: %d", id)
return &dbConn{id}, nil
}
func main() {
//计数信号量
var wg sync.WaitGroup
//同时并发的数量
wg.Add(maxGoroutines)
myPool, _ := New(createConn, poolSize)
//开25个goroutine同时查询
for i := 0; i < maxGoroutines; i++ {
//模拟请求
time.Sleep(time.Duration(rand.Intn(2)) * time.Second)
go func(gid int) {
execQuery(gid, myPool)
wg.Done()
}(i)
}
//等待上面开的goroutine结束
wg.Wait()
myPool.Close()
} //定义一个查询方法,参数是当前gorotineId和资源池
func execQuery(goroutineId int, pool *Pool) {
//从池里请求资源,第一次肯定是没有的,就会创建一个dbConn实例
conn, _ := pool.Acquire()
//将创建的dbConn实例放入了资源池的缓冲通道里
defer pool.Release(conn)
//睡眠一下,模拟查询过程
time.Sleep(time.Duration(rand.Intn(10)) * time.Second)
log.Printf("执行查询...协程ID [%d] 资源ID [%d]", goroutineId, conn.(*dbConn).ID)
}

[Go] golang缓冲通道实现资源池的更多相关文章
- [Go] golang缓冲通道实现管理一组goroutine工作
通道1.当一个资源需要在goroutine之间共享时,通道在goroutine之间架起了一个管道2.无缓冲通道和有缓冲通道,make的第二个参数就是缓冲区大小3.无缓冲通道需要发送和接收都准备好,否则 ...
- golang channel无缓冲通道会发生阻塞的验证
公司搞了午间技术par,本周我讲的主题是关于无缓冲通道channel是否会发生阻塞,并进行了验证. go语言中channel分为无缓冲通道和有缓冲通道两种 channel提供了一种在goroutine ...
- [Go] golang无缓冲通道实现工作池控制并发
展示如何使用无缓冲的通道创建一个goroutine池,控制并发频率1.无缓冲通道保证了两个goroutine之间的数据交换2.当所有的goroutine都忙的时候,能够及时通过通道告知调用者3.无缓冲 ...
- Golang并发编程有缓冲通道和无缓冲通道(channel)
无缓冲通道 是指在接收前没有能力保存任何值得通道.这种类型的通道要求发送goroutine和接收goroutine同时准备好,才能完成发送和接收操作.如果两个goroutine没有同时准备好,通道会导 ...
- golang中为何在同一个goroutine中使用无缓冲通道会导致死锁
package main import "fmt" func main() { /* 以下程序会导致死锁 c := make(chan int) c <- 10 n1 := ...
- Go语言的通道(2)-缓冲通道
有缓冲的通道相比于无缓冲通道,多了一个缓存的功能,如下图描述的一样: 从图上可以明显看到和无缓冲通道的区别,无缓冲必须两个Goroutine都进入通道才能进行数据的交换,这个不用,如果数据有,直接就能 ...
- Go语言的通道(1)-无缓冲通道
前言: 上文中我们采用了[原子函数]已经[共享锁]两种方式分别对多个goroutine进行了同步,但是在go语言中提供了另一种更好的方式,那就是使用通道(Channel). 一.通道是什么? 其实无论 ...
- [GO]有缓冲通道
有缓冲通道就是在有能力保留数据的通道,那么通道在满的时候或者通道是空的时候,存数据和取数据就会发生阻塞 package main import ( "fmt" "time ...
- [GO]无缓冲通道(unbuffered channel)
无缓冲通道(unbuffered channel)是指在接收前没有能力保存任何值的通道,在之前的例子中使用的都是无缓冲通道,需要注意的是,对于无缓冲通道而言,不管是往通道里写数据还是从通道里读数据,都 ...
随机推荐
- 771. Jewels and Stones
You're given strings J representing the types of stones that are jewels, and S representing the ston ...
- Oracle 的开窗函数 rank,dense_rank,row_number
1.开窗函数和分组函数的区别 分组函数是指按照某列或者某些列分组后进行某种计算,比如计数,求和等聚合函数进行计算. 开窗函数是指基于某列或某些列让数据有序,数据行数和原始数据数相同,依然能曾现个体数据 ...
- Python3.x在linux下print中文问题
由于python3内部以Unicode实现,在默认非utf-8的Linux上print中文会报错UnicodeEncodeError. 由于系统默认非unicode,python3又以unicode实 ...
- 【安富莱原创开源应用第1期】花式玩转网络摄像头之TCP上位机软件实现,高端大气上档次,速度2MB/S,华丽丽的界面效果
说明:1.例子是两年前做的,一直没有顾上整理出来,今天特地整理出来,开源出来给大家玩.2.上位机是emWin模拟器开发的,大家估计很难猜到,所以你会emWin话的,就可以轻松制作上位机.做些通信和控制 ...
- Atlas实现MySQL大表部署读写分离
序章 Atlas是360团队弄出来的一套基于MySQL-Proxy基础之上的代理,修改了MySQL-Proxy的一些BUG,并且优化了很多东西.而且安装方便.配置的注释写的蛮详细的,都是中文.英文不好 ...
- [Swift]LeetCode208. 实现 Trie (前缀树) | Implement Trie (Prefix Tree)
Implement a trie with insert, search, and startsWith methods. Example: Trie trie = new Trie(); trie. ...
- [Swift]LeetCode675. 为高尔夫比赛砍树 | Cut Off Trees for Golf Event
You are asked to cut off trees in a forest for a golf event. The forest is represented as a non-nega ...
- 简单python程序练习
1.打印※花矩形 i = 1 while i<=5: j = 1 while j <=5: print("*",end="") #end=" ...
- 优化之Joiner组件
Joiner组件在运行时需要额外的内存空间处理中间结果,因此会影响性能 可通过查看Joiner performance计数器来决定Joiner组件是否需要优化 通过如下方式优化Joiner组件 将Ma ...
- 【java提高】---queue集合
queue集合 什么是Queue集合? 答:Queue用于模拟队列这种数据结构.队列通常是指“先进先出(FIFO)”的容器.队列的头部保存在队列中存放时间最长的元素,尾部保存存放时间最短的元素. 新元 ...