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缓冲通道实现资源池的更多相关文章

  1. [Go] golang缓冲通道实现管理一组goroutine工作

    通道1.当一个资源需要在goroutine之间共享时,通道在goroutine之间架起了一个管道2.无缓冲通道和有缓冲通道,make的第二个参数就是缓冲区大小3.无缓冲通道需要发送和接收都准备好,否则 ...

  2. golang channel无缓冲通道会发生阻塞的验证

    公司搞了午间技术par,本周我讲的主题是关于无缓冲通道channel是否会发生阻塞,并进行了验证. go语言中channel分为无缓冲通道和有缓冲通道两种 channel提供了一种在goroutine ...

  3. [Go] golang无缓冲通道实现工作池控制并发

    展示如何使用无缓冲的通道创建一个goroutine池,控制并发频率1.无缓冲通道保证了两个goroutine之间的数据交换2.当所有的goroutine都忙的时候,能够及时通过通道告知调用者3.无缓冲 ...

  4. Golang并发编程有缓冲通道和无缓冲通道(channel)

    无缓冲通道 是指在接收前没有能力保存任何值得通道.这种类型的通道要求发送goroutine和接收goroutine同时准备好,才能完成发送和接收操作.如果两个goroutine没有同时准备好,通道会导 ...

  5. golang中为何在同一个goroutine中使用无缓冲通道会导致死锁

    package main import "fmt" func main() { /* 以下程序会导致死锁 c := make(chan int) c <- 10 n1 := ...

  6. Go语言的通道(2)-缓冲通道

    有缓冲的通道相比于无缓冲通道,多了一个缓存的功能,如下图描述的一样: 从图上可以明显看到和无缓冲通道的区别,无缓冲必须两个Goroutine都进入通道才能进行数据的交换,这个不用,如果数据有,直接就能 ...

  7. Go语言的通道(1)-无缓冲通道

    前言: 上文中我们采用了[原子函数]已经[共享锁]两种方式分别对多个goroutine进行了同步,但是在go语言中提供了另一种更好的方式,那就是使用通道(Channel). 一.通道是什么? 其实无论 ...

  8. [GO]有缓冲通道

    有缓冲通道就是在有能力保留数据的通道,那么通道在满的时候或者通道是空的时候,存数据和取数据就会发生阻塞 package main import ( "fmt" "time ...

  9. [GO]无缓冲通道(unbuffered channel)

    无缓冲通道(unbuffered channel)是指在接收前没有能力保存任何值的通道,在之前的例子中使用的都是无缓冲通道,需要注意的是,对于无缓冲通道而言,不管是往通道里写数据还是从通道里读数据,都 ...

随机推荐

  1. java points[复习]

    1 - & 与 && 的区别: &:不管左边是true还是false,右端都会进行运算: &&:当左端为false时,右端不再进行运算: 即在与运算时, ...

  2. Live2D插件--漂浮的二次元小姐姐

    这个插件找了很久,都没找到,今天偶然翻到一个小哥的博客发现了这个,果断偷走. 教程转自简书:https://www.jianshu.com/p/1cedcf183633 还有这些,你可能有用 修改位置 ...

  3. mybatis的配置和使用

    mybatis的配置和使用 MyBatis 是支持定制化 SQL.存储过程以及高级映射的优秀的持久层框架.MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集.MyBatis ...

  4. Edge-assisted Traffic Engineering and applications in the IoT

    物联网中边缘辅助的流量工程和应用 本文为SIGCOMM 2018 Workshop (Mobile Edge Communications, MECOMM)论文. 笔者翻译了该论文.由于时间仓促,且笔 ...

  5. Fragment中TabLayout加FragmentViewPager

    让tablayout中的Tab平铺:在tablayout布局中写: app:tabMaxWidth="0dp"app:tabGravity="fill"app: ...

  6. Swift 对象内存模型探究(一)

    本文来自于腾讯Bugly公众号(weixinBugly),未经作者同意,请勿转载,原文地址:https://mp.weixin.qq.com/s/zIkB9KnAt1YPWGOOwyqY3Q 作者:王 ...

  7. vue+mescroll=VScrollFull

    VScrollFull 介绍 这个组件是什么? 是为了方便的使用下拉刷新,上拉加载而去封装的一个依赖于 mescroll.js 的 vue 组件(未发布,文末代码~) 封装这个组件使用了什么? mes ...

  8. [Swift]LeetCode407. 接雨水 II | Trapping Rain Water II

    Given an m x n matrix of positive integers representing the height of each unit cell in a 2D elevati ...

  9. Mysql数据库和表的增删改查以及数据备份&恢复

    数据库 查看所有数据库 show databases; 使用数据库 use 数据库名; 查看当前使用的数据库 select database(); 创建数据库 create database 数据库名 ...

  10. Kubernetes 笔记 07 豌豆荚之旅(二)

    本文首发于我的公众号 Linux云计算网络(id: cloud_dev),专注于干货分享,号内有 10T 书籍和视频资源,后台回复「1024」即可领取,欢迎大家关注,二维码文末可以扫. Hi,大家好, ...