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. 微信小程序开发---各代码文件简介

    根据上一文,已建立QuickStart 项目,该项目系本人毕设部分内容,所以记录以便以后查阅 开发小程序就必须了解小程序项目目录结构和文件作用,接下来就根据我现在自学得到的知识把这些记录下来. 一.目 ...

  2. Java课堂笔记(零):内容索引

    回想自己学习和使用Java的时间也是很长了.本科期间课堂上浅尝辄止地学习了点皮毛,后来也是搁置不用,未曾深入研究.研究生期间因为项目和实习的原因,基本算是重新拾起Java这门语言,并且接触到了Spri ...

  3. 马哥k8s

    https://pan.baidu.com/s/1BAX-j54bLcmWF-0ei-c-pw 31y6

  4. NET Core微服务之路:基于Ocelot的API网关Relay实现--RPC篇

    前言 我们都知道,API网关是工作在应用层上网关程序,为何要这样设计呢,而不是将网关程序直接工作在传输层.或者网络层等等更底层的环境呢?让我们先来简单的了解一下TCP/IP的五层模型.     (图片 ...

  5. Kubernetes — 我的第一个容器化应用

    而在这篇文章中,我们就来扮演一个应用开发者的角色,使用这个 Kubernetes 集群发布第一个容器化应用. 在开始实践之前,我先给你讲解一下 Kubernetes 里面与开发者关系最密切的几个概念. ...

  6. 使用cloudreve搭建个人网盘

    这次将腾迅的对象存储cos挂载到了服务器上,就想自己搭建个网盘,虽然每月50G的空间和10G流量,也够用了 之前写过使用owncloud来搭建个人网盘,使用起来挺方便,就是不知道为什么感觉打开速度慢, ...

  7. [Swift]LeetCode285. 二叉搜索树中的中序后继节点 $ Inorder Successor in BST

    Given a binary search tree and a node in it, find the in-order successor of that node in the BST. Th ...

  8. [Swift]LeetCode492. 构造矩形 | Construct the Rectangle

    For a web developer, it is very important to know how to design a web page's size. So, given a speci ...

  9. [Swift]LeetCode832. 翻转图像 | Flipping an Image

    Given a binary matrix A, we want to flip the image horizontally, then invert it, and return the resu ...

  10. 基于ipv6的数据抓包

    一.实验拓扑 二.配置过程 以r1为例 R1: R1(config)#int f0/0 R1(config-if)#ipv6 enable R1(config-if)#ipv6 address 200 ...