[记录]bug记录与小结--[race condition]

最近接手一个server的新功能开发,遇到很多问题。这些问题在同行看来,这些问题都是小儿科了。但是,对我第一次开发服务器程序来说,被这些问题折磨很久了,并且最终找出原因的,印象深刻,不能忘怀。暂记录于此,以备回忆。以下就分条叙述吧.

1) 由于是从一个遗留系统上开发新功能,对于系统上原有的代码,我没有过多的去修改。我的本意是用最小的修改,完成新功能的开发。理由是,这样做风险最小。后来事实证明,这也只是我一厢情愿了。

2) 程序并发问题。在我的程序中有个场景是这样的,线程池中某个线程将一个请求包发送到其他多个server,采用异步方式,在回调函数中处理后续事情。只有多个server都回复我后,才能判断本次请求是完成情况。所以,对于一个请求来说,有一个共享数据,该数据采用引用计数的来保护。在请求包通过异步接口发送出去后,就不能当前进程中再次访问该共享数据了。因为,这些共享数据可能已经被释放了,即回调函数全部执行过了。这是一个坑,我的程序在这里core掉很多次。为了方便说明,这里简单写个示例代码:

request *req = get_request();
std::vector<data_server*> servers;
for (int i = 0; i < servers.size(); ++i)
{
     servers[i].send(req, ASYNC);
}
//TODO access req.

上述代码中,异步方式将请求包发送出去后,在TODO后面访问req,比如打印一条日志,将req的部分信息打印出来。这个地方在并发量高的情况,是百分之百要core掉。因为异步发送后,该线程可能被抢占。而该请求的回调函数会执行,执行后,这个请求包就释放掉了。再次访问这个req当然就出问题了。

那为啥不为每一个server准备一个req呢,不让其共享数据。其实,由于我需要在每一个server都回复我后,再做其他事情,所以共享数据是必须的。

这个bug查找的时候也是费尽力气。每次core后,通过gdb无法获取req的状态变化。core的时候,总是显示req地址为0x0. 表示req已经被释放掉了。所以,我用了最笨的办法,在req经过的每个函数里,都尽可能地将其状态记录在函数的局部变量中。这招果然管用,当core掉后,查看core文件,一路跟踪req的状态变化。终于找到了core的原因了。


[后记] 其实,这个是个典型的bug——race condition。在并发程序中,需要非常仔细,避免出现race condition。我没有找到关于race condition的一个准确的定义。简单描述为:在并发环境中,多个线程(进程)访问共享数据,并且至少有一个线程修改共享数据。如何检测race condition? 检测race condtion是一个NP问题。所以,就不要指望有啥工具了吧,还是自己细心与耐心了。如何处理race condition? 对于共享数据而言,通过加锁的方式,将并发访问共享数据串行化。


对于在我项目中的问题,由于为每一请求创建了共享数据,如果都用加锁的方式串行化的话,势必开销很大。而这里的共享数据保存一个状态信息,用原子变量来存储。这样,就去掉了加锁的开销,同时,采用引用计数办法,避免了race condition.

3)内存被重复释放。引用计数实现不正确,导致内存被重复释放。
以上,就是本次开发过程中遇到的一些印象比较深刻的问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值