专栏首页Golang语言社区终于遇到goroutine死锁的BUG了

终于遇到goroutine死锁的BUG了

今天测试用Go语言写的角色服务器,发现在模拟大量客户端获取角色列表的时候会卡住,但是服务器程序的CPU占用率为零。分析并经过代码检查确认是goroutine死锁。

此问题涉及到的代码主要是一个database类,封装了角色数据的一些数据库操作。虽然sql.DB后台支持连接缓冲池,但是为了限制资源的使用,我仍然在database类里做了并发数的限制,角色数据的每个操作必须先获得令牌才能进行后续操作,操作完成后归还令牌。代码主要结构如下所示:

import "database/sql"

const DBCONN_MAXCOUNT = 128

type database struct {

db *sql.DB

tokens chan int

}

func (p *database) Open(source string) error {

db, err := sql.Open("mysql", source) if err != nil {

return err

} p.db = db p.tokens = make(chan int, DBCONN_MAXCOUNT) for i := 0; i < DBCONN_MAXCOUNT; i++ {

p.tokens <- 1

} return nil

}

func (p *database) Close() {

p.db.Close()

}

func (p *database) GetRoleData(name string) ([]byte, error) {

token := <-p.tokens defer func() {p.tokens<-token}() //相关数据库操作...

}

func (p *database) DeleteRole(name string) error {

token := <-p.tokens defer func() {p.tokens<-token}() //相关数据库操作...

}

BUG的原因是在DeleteRole函数里其实进行了一些数据库操作,包括为了检查角色数据是否需要放到回收库需要先获取角色数据检查角色的等级等。其中获取角色数据就调用到了GetRoleData函数。这就导致一种可能性:如果所有客户端都执行到DeleteRole函数里,准备执行GetRoleData函数时没有可用令牌,就会集体陷入死锁。

解决办法是定义了一个不需要更底层的函数getRoleData,供GetRoleData和DeleteRole函数调用,从而不需要申请多个令牌。

func (p *database) getRoleData(name string) ([]byte, error) {

//相关数据库操作...

}

func (p *database GetRoleData(name string) ([]byte, error) {

token := <-p.tokens defer func() {p.tokens<-token}() return p.getRoleData(name)

}

func (p *database) DeleteRole(name string) error {

token := <-p.tokens defer func() {p.tokens<-token}() data, err := p.getRoleData(name) if err != nil {

return err

} //其他操作...

}

总结:

1.最好不要设计成做一件事情需要获取N个资源,然后依次去申请。如果不能避免申请多个资源,也应该按照固定次序去申请,否则会导致死锁(本例中是不小心导致的,因为申请令牌是后加的)。这是所有编程语言设计并发程序的通用规则。

2.函数分级。一个类(结构)的暴露给类(结构)外部使用的函数最好不要相互调用。如果有共用代码,可以提取为更底层的一个内部函数,供给多个上一级的函数使用。这个不处理好容易导致以后调整功能需要修改代码时出问题。

本文分享自微信公众号 - Golang语言社区(Golangweb)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2016-09-17

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Gopher面试中的Coding

    从四月份下半月开始,陆陆续续面试了几家公司,都是golang的岗位。每一次面试,侧重点都会有不同,有的会直接给过来一道试题, 然后边解题,边讲述自己的思路,然后...

    李海彬
  • Golang 多goroutine异步通知error的一种方法

    作者近期在写一个项目时遇到了这样的需求:调用一个库API函数,函数内部又会拉起若干个后台goroutine。这时后台goroutine如果遇到错误想要及时通知库...

    李海彬
  • Gopher面试中的Coding

    从四月份下半月开始,陆陆续续面试了几家公司,都是golang的岗位。每一次面试,侧重点都会有不同,有的会直接给过来一道试题, 然后边解题,边讲述自己的思路,然后...

    李海彬
  • Job 资源对象

    服务类的Pod容器:RC、RS、DS、Deployment.(Pod内运行的服务,要持续运行) 工作类的Pod容器:Job--->执行一次,或者批量执行处理程序...

    小手冰凉
  • Gopher面试中的Coding

    从四月份下半月开始,陆陆续续面试了几家公司,都是golang的岗位。每一次面试,侧重点都会有不同,有的会直接给过来一道试题, 然后边解题,边讲述自己的思路,然后...

    李海彬
  • “设计应对变化”--实例讲解一个数据同步系统

     系列文章索引: [WCF邮件通信系统应用 之 数据同步程序 之 设计内幕 之 一] 同步一个数据库要发多少个数据包? [WCF邮件通信系统应用 之 数据同步...

    用户1177503
  • 来,我们一起打造一款代码命名工具

    那么如何更好的命名呢? 是否有好的工具可以支持我们命名呢?网上搜索一圈没有发现满意的,于是自己动手丰衣足食,https://jadepeng.gitee.io/...

    用户1177380
  • 我赌你不清楚Spring中关于Null的这些事

    之前一直在某些代码中看到过使用@Nullable 标注过的注释,当时也没有在意到底是什么意思,之后忍不住去调查一番,这篇文章来谈谈Spring中关于Null的那...

    cxuan
  • Gopher面试中的Coding

    从四月份下半月开始,陆陆续续面试了几家公司,都是golang的岗位。每一次面试,侧重点都会有不同,有的会直接给过来一道试题, 然后边解题,边讲述自己的思路,然后...

    李海彬
  • NTP网络时间服务器系统时钟同步系统搭建

        NTP(Network Time Protocol,网络时间协议)是用来使网络中的各个计算机时间同步的一种协议。

    NTP网络同步时钟

扫码关注云+社区

领取腾讯云代金券