[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)是指在接收前没有能力保存任何值的通道,在之前的例子中使用的都是无缓冲通道,需要注意的是,对于无缓冲通道而言,不管是往通道里写数据还是从通道里读数据,都 ...
随机推荐
- cadence电源和地平面的处理
覆铜是PCB布线的常用操作,下面总结覆铜的方法以及电源层分割的方法 PCB设计中,经常面临电源.地噪声的挑战,在高速数字系统中,电源和地的设计非常关键!电源和地的主要作用有: 一,为数字信号提供稳定的 ...
- ThreadLocal是否会导致内存泄露
什么是内存泄露? 维基百科的定义:[内存泄漏指由于疏忽或错误造成程序未能释放已经不再使用的内存],我的理解就是程序失去了对某段内存的控制,那么这段内存就算是泄露了. ThreadLocal为什么会导致 ...
- boot+Xss防攻击的处理方案
以下是boot+Xss防攻击的(解决处理JSON入参)处理方案,第二个亲测有效 https://www.jianshu.com/p/3e4b00b8ff3ahttps://www.jianshu.co ...
- ueditor编辑器多图上传为什么顺序打乱了
我上一个版本用的是ueditor1.3.6,自从1.4.2版以后,“前端上传模块统一改用webuploader”,ueditor在多图上传一直考虑漏掉了图片顺序的问题. 我的网站在用户上传图片文章的时 ...
- nginx配置ssl证书实现https访问
一,环境说明 服务器系统:ubuntu16.04LTS 服务器IP地址:47.89.12.99 域名:bjubi.com 二,域名解析到服务器 在阿里云控制台-产品与服务-云解析DNS-找到需要解析的 ...
- 实战经验丨PHP反序列化漏洞总结
又到了金三银四跳槽季,很多小伙伴都开始为面试做准备,今天小编就给大家分享一个网安常见的面试问题:PHP反序列化漏洞. 虽然PHP反序列化漏洞利用的条件比较苛刻,但是一旦被利用就会产生很严重的后果,所以 ...
- [SQL]LeetCode177. 第N高的薪水 | Nth Highest Salary
Write a SQL query to get the nth highest salary from the Employee table. +----+--------+ | Id | Sala ...
- [Swift]LeetCode416. 分割等和子集 | Partition Equal Subset Sum
Given a non-empty array containing only positive integers, find if the array can be partitioned into ...
- [Swift]LeetCode688. “马”在棋盘上的概率 | Knight Probability in Chessboard
On an NxN chessboard, a knight starts at the r-th row and c-th column and attempts to make exactly K ...
- [Swift]LeetCode789. 逃脱阻碍者 | Escape The Ghosts
You are playing a simplified Pacman game. You start at the point (0, 0), and your destination is (ta ...