返回顶部
首页 > 资讯 > 数据库 >Go语言之并发示例-Pool(二)
  • 601
分享到

Go语言之并发示例-Pool(二)

2024-04-02 19:04:59 601人浏览 独家记忆
摘要

针对这个资源池管理的一步步都实现了,而且做了详细的讲解,下面就看下整个示例代码,方便理解。package commonimport (     "

针对这个资源池管理的一步步都实现了,而且做了详细的讲解,下面就看下整个示例代码,方便理解。

package commonimport (
    "errors"
    "io"
    "sync"
    "log")//一个安全的资源池,被管理的资源必须都实现io.Close接口type Pool struct {
    m       sync.Mutex
    res     chan io.Closer
    factory func() (io.Closer, error)
    closed  bool}var ErrPoolClosed = errors.New("资源池已经被关闭。")//创建一个资源池func New(fn func() (io.Closer, error), size uint) (*Pool, error) {
    if size <= 0 { 
           return nil, errors.New("size的值太小了。")
    }    
    return &Pool{
        factory: fn,
        res:     make(chan io.Closer, size),
    }, nil}//从资源池里获取一个资源func (p *Pool) Acquire() (io.Closer,error) { 
   select {
       case r,ok := <-p.res:
        log.Println("Acquire:共享资源")       
        if !ok {
                    return nil,ErrPoolClosed
        }        
        return r,nil
    default:
        log.Println("Acquire:新生成资源")        
        return p.factory()
    }}
 //关闭资源池,释放资源func (p *Pool) Close() {
    p.m.Lock()
    defer p.m.Unlock()    
    if p.closed { 
           return
    }

    p.closed = true

    //关闭通道,不让写入了
    close(p.res)    //关闭通道里的资源
    for r:=range p.res {
        r.Close()
    }}func (p *Pool) Release(r io.Closer){
    //保证该操作和Close方法的操作是安全的
    p.m.Lock()    
    defer p.m.Unlock()   

     //资源池都关闭了,就省这一个没有释放的资源了,释放即可
    if p.closed {
        r.Close()        
        return 
    }    
    select {
    case p.res <- r:
        log.Println("资源释放到池子里了")    
    default:
        log.Println("资源池满了,释放这个资源吧")
        r.Close()
    }
}


好了,资源池管理写好了,也知道资源池是如何实现的啦,现在我们看看如何使用这个资源池,模拟一个数据库连接池吧。

package mainimport (
    "flysnow.org/hello/common"
    "io"
    "log"
    "math/rand"
    "sync"
    "sync/atomic"
    "time")const (    
    //模拟的最大Goroutine
    maxGoroutine = 5
    //资源池的大小
    poolRes      = 2)func main() {    
    //等待任务完成
    var wg sync.WaitGroup
    wg.Add(maxGoroutine)

    p, err := common.New(createConnection, poolRes)    

    if err != nil {
        log.Println(err)        
        return
    }    
    //模拟好几个goroutine同时使用资源池查询数据
    for query := 0; query < maxGoroutine; query++ { 
       go func(q int) {
            dbQuery(q, p)
            wg.Done()
        }(query)
    }

    wg.Wait()
    log.Println("开始关闭资源池")
    p.Close()}//模拟数据库查询func dbQuery(query int, pool *common.Pool) {
    conn, err := pool.Acquire()
    if err != nil {
        log.Println(err)       
         return
    }    
    defer pool.Release(conn)    
    //模拟查询
    time.Sleep(time.Duration(rand.Intn(1000)) * time.Millisecond)
    log.Printf("第%d个查询,使用的是ID为%d的数据库连接", query, conn.(*dbConnection).ID)}//数据库连接type dbConnection struct {
    ID int32//连接的标志}//实现io.Closer接口func (db *dbConnection) Close() error {
    log.Println("关闭连接", db.ID)    
    return nil}var idCounter int32//生成数据库连接的方法,以供资源池使用func createConnection() (io.Closer, error) {   
    //并发安全,给数据库连接生成唯一标志
    id := atomic.AddInt32(&idCounter, 1)    
    return &dbConnection{id}, nil
}


这时我们测试使用资源池的例子,首先定义了一个结构体dbConnection,它只有一个字段,用来做唯一标记。然后dbConnection实现了io.Closer接口,这样才可以使用我们的资源池。


createConnection函数对应的是资源池中的factory字段,用来创建数据库连接dbConnection的,同时为其赋予了一个为止的标志。


接着我们就同时开了 5 个goroutine,模拟并发的数据库查询dbQuery,查询方法里,先从资源池获取可用的数据库连接,用完后再释放。


这里我们会创建 5 个数据库连接,但是我们设置的资源池大小只有 2 ,所以再释放了 2 个连接后,后面的 3 个连接会因为资源池满了而释放不了,一会我们看下输出的打印信息就可以看到。


最后这个资源连接池使用完之后,我们要关闭资源池,使用资源池的Close方法即可。


2017/04/17 22:25:08 Acquire:新生成资源
2017/04/17 22:25:08 Acquire:新生成资源
2017/04/17 22:25:08 Acquire:新生成资源
2017/04/17 22:25:08 Acquire:新生成资源
2017/04/17 22:25:08 Acquire:新生成资源
2017/04/17 22:25:08 第2个查询,使用的是ID为4的数据库连接
2017/04/17 22:25:08 资源释放到池子里了
2017/04/17 22:25:08 第4个查询,使用的是ID为1的数据库连接
2017/04/17 22:25:08 资源释放到池子里了
2017/04/17 22:25:08 第3个查询,使用的是ID为5的数据库连接
2017/04/17 22:25:08 资源池满了,释放这个资源吧
2017/04/17 22:25:08 关闭连接 5
2017/04/17 22:25:09 第1个查询,使用的是ID为3的数据库连接
2017/04/17 22:25:09 资源池满了,释放这个资源吧
2017/04/17 22:25:09 关闭连接 3
2017/04/17 22:25:09 第0个查询,使用的是ID为2的数据库连接
2017/04/17 22:25:09 资源池满了,释放这个资源吧
2017/04/17 22:25:09 关闭连接 2
2017/04/17 22:25:09 开始关闭资源池
2017/04/17 22:25:09 关闭连接 4
2017/04/17 22:25:09 关闭连接 1


到这里,我们已经完成了一个资源池的管理,并且进行了使用测试。


资源对象池的使用比较频繁,因为我们想把一些对象缓存起来,以便使用,这样就会比较高效,而且不会经常调用GC,为此Go为我们提供了原生的资源池管理,防止我们重复造轮子,这就是sync.Pool,我们看下刚刚我们的例子,如果用sync.Pool实现。


package mainimport (
    "log"
    "math/rand"
    "sync"
    "sync/atomic"
    "time")const (    
    //模拟的最大goroutine
    maxGoroutine = 5)func main() {
    //等待任务完成
    var wg sync.WaitGroup
    wg.Add(maxGoroutine)

    p:=&sync.Pool{
        New:createConnection,
    }   
     //模拟好几个goroutine同时使用资源池查询数据
    for query := 0; query < maxGoroutine; query++ {
      go func(q int) {
            dbQuery(q, p)
            wg.Done()
        }(query)
    }

    wg.Wait()}//模拟数据库查询
func dbQuery(query int, pool *sync.Pool) {    conn:=pool.Get().(*dbConnection)        defer pool.Put(conn)        //模拟查询    time.Sleep(time.Duration(rand.Intn(1000)) * time.Millisecond)    log.Printf("第%d个查询,使用的是ID为%d的数据库连接", query, conn.ID)}//数据库连接
type dbConnection struct {    ID int32//连接的标志}//实现io.Closer接口
func (db *dbConnection) Close() error {    log.Println("关闭连接", db.ID)    return nil}var idCounter int32//生成数据库连接的方法,以供资源池使用
func createConnection() interface{} {      //并发安全,给数据库连接生成唯一标志    id := atomic.AddInt32(&idCounter, 1)    return &dbConnection{ID:id}
}


进行微小的改变即可,因为系统库没有提供New这类的工厂函数,所以我们使用字面量创建了一个sync.Pool,注意里面的New字段,这是一个返回任意对象的方法,类似我们自己实现的资源池中的factory字段,意思都是一样的,都是当没有可用资源的时候,生成一个。


这里我们留意到系统的资源池是没有大小限制的,也就是说默认情况下是无上限的,受内存大小限制。


资源的获取和释放对应的方法是Get和Put,也很简洁,返回任意对象interface{}。


2017/04/17 22:42:43 第0个查询,使用的是ID为2的数据库连接
2017/04/17 22:42:43 第2个查询,使用的是ID为5的数据库连接
2017/04/17 22:42:43 第4个查询,使用的是ID为1的数据库连接
2017/04/17 22:42:44 第3个查询,使用的是ID为4的数据库连接
2017/04/17 22:42:44 第1个查询,使用的是ID为3的数据库连接


关于系统的资源池,我们需要注意的是它缓存的对象都是临时的,也就说下一次GC的时候,这些存放的对象都会被清除掉。


您可能感兴趣的文档:

--结束END--

本文标题: Go语言之并发示例-Pool(二)

本文链接: https://lsjlt.com/news/42345.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • Go语言之并发示例-Pool(二)
    针对这个资源池管理的一步步都实现了,而且做了详细的讲解,下面就看下整个示例代码,方便理解。package commonimport (     "...
    99+
    2024-04-02
  • Go语言中并发goroutine底层原理的示例分析
    小编给大家分享一下Go语言中并发goroutine底层原理的示例分析,希望大家阅读完这篇文章之后都有所收获,下面让我们一起去探讨吧!一、基本概念①并发、并行区分1.概念并发:同一时间段内一个对象执行多个任务,充分利用时间并行:同一时刻,多个...
    99+
    2023-06-29
  • Go语言并发之原子操作详解
    目录修改赋值与读取比较并交换小结代码中的加锁操作因为涉及内核态的上下文切换会比较耗时、代价比较高。针对基本数据类型我们还可以使用原子操作来保证并发安全,因为原子操作是Go语言提供的方...
    99+
    2022-12-29
    Go语言 并发 原子操作 Go语言 原子操作 Go语言 并发
  • go语言如何并发
    这篇文章主要介绍“go语言如何并发”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“go语言如何并发”文章能帮助大家解决问题。Go语言通过编译器运行时(runtime),从语言上支持了并发的特性;并发是...
    99+
    2023-07-05
  • Go语言通过WaitGroup实现控制并发的示例详解
    目录与Channel区别基本使用示例完整代码特别提示多任务示例完整代码与Channel区别 Channel能够很好的帮助我们控制并发,但是在开发习惯上与显示的表达不太相同,所以在Go...
    99+
    2023-01-30
    Go语言 WaitGroup控制并发 Go语言 WaitGroup
  • GO语言并发之好用的sync包详解
    目录sync.Map 并发安全的Mapsync.Once 只执行一次sync.Cond 条件变量控制小结sync.Map 并发安全的Map 反例如下,两个Goroutine分别读写。...
    99+
    2022-12-29
    GO语言 并发 sync包 GO语言 sync包 GO sync包
  • 详解go语言的并发
    目录1、启动go语言的协程2、runtime.Goexit()方法。立即终止当前的协程3、runtime.GOMAXPROCS()表示go使用几个cpu执行代码4、管道定义和创...
    99+
    2022-06-07
    详解go语言 GO 并发 go语言
  • Go语言基础并发channel
    这篇文章主要讲解了“Go语言基础并发channel”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Go语言基础并发channel”吧!为什么需要channel...
    99+
    2024-04-02
  • Go语言并发编程 sync.Once
    sync.Once用于保证某个动作只被执行一次,可用于单例模式中,比如初始化配置。我们知道init()函数也只会执行一次,不过它是在main()函数之前执行,如果想要在代码执行过程中...
    99+
    2024-04-02
  • Go语言开发保证并发安全实例详解
    目录什么是并发安全?Mutex悲观锁乐观锁版本号机制CAS互斥锁读写互斥锁什么是并发安全? 在高并发场景下,进程、线程(协程)可能会发生资源竞争,导致数据脏读、脏写、死锁等问题,为了...
    99+
    2024-04-02
  • 编程语言之高并发系统中限流的示例分析
    这篇文章主要介绍了编程语言之高并发系统中限流的示例分析,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。在开发高并发系统时有三把利器用来保护系统:缓存、降级和限流。本文结合作者的...
    99+
    2023-05-30
    编程语言
  • Go语言并发编程指南
    Go语言是一种开源的编程语言,它被设计用于构建简单、可靠和高效的软件。其中最显著的特点之一便是其强大的并发编程能力。通过Go语言的并发特性,可以更好地利用多核处理器,实现高效的并行处理...
    99+
    2024-04-02
  • Go语言并发特性解析
    Go语言并发特性解析 Go语言作为一种由Google开发的开源编程语言,在处理并发编程方面拥有独特的优势。由于其简洁、高效和强大的并发机制,Go语言越来越受到开发者的青睐。本文将深入探...
    99+
    2024-04-02
  • go语言beego框架web开发语法笔记示例
    目录两个跳转语法模型创建获取post请求传过来的值获取字符串获取文件获取文件后缀orm查询表所有数据前端循环语法前端格式化时间前端url传值方式两个跳转语法 第一个参数是请求路径,第...
    99+
    2024-04-02
  • 【案例】常驻查询引发的thread pool 性能问题之二
    一 现象     某业务单机4个实例中的一个实例出现连接数远高于其他三个实例(正常是4K,问题实例是8K+),但是这4个实例的配置完全相同。业务开发反馈为部分连接失败。&nbs...
    99+
    2024-04-02
  • 探讨Go语言并发和并行之间的区别及联系
    Go语言是一种由Google开发的编程语言,以其并发编程特性而闻名。在Go语言中,有两个重要的概念——并发和并行。虽然这两个概念看起来类似,但它们之间有着微妙的区别和联系。本文将探讨G...
    99+
    2024-03-12
    go语言 并发 并行
  • Go语言select语句用法示例
    目录用法使用场景实现收发功能注意事项用法 多个通道 Channel 中信息的发送和接受处理的专用的语句—select 语句。select 语句会阻塞,直到其中的一个发送/...
    99+
    2024-04-02
  • Go语言基础学习之map的示例详解
    目录Mapmap定义map基本使用判断某个键是否存在map的遍历使用delete()函数删除键值对按照指定顺序遍历map元素为map类型的切片值为切片类型的mapMap实现原理什么是...
    99+
    2023-05-14
    Golang map原理 Golang map实现 Golang map
  • Go语言题解LeetCode1260二维网格迁移示例详解
    目录题目描述示例 1:示例 2:示例 3:思路分析AC 代码题目描述 1260. 二维网格迁移 - 力扣(LeetCode) 给你一个 m 行 n 列的二维网格&nbs...
    99+
    2023-01-05
    Go语言二维网格迁移 Go LeetCode
  • Go语言中的并发是什么
    这篇文章主要介绍“Go语言中的并发是什么”,在日常操作中,相信很多人在Go语言中的并发是什么问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Go语言中的并发是什么”的疑惑有所帮...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作