使用ACE+TAO导致的多线程死锁问题

利用CORBA+TAO可以很方便的建立起分布式的应用,一个典型的应用是1个Server端和多个Client端进行双向通信。
这个时候要特别注意防止产生并发处理不当造成死锁问题。
我在项目(使用ACE+TAO-5.2.5+1.2.5)中就碰到这样一个问题,折磨了我好几天,最后才发现原因。

注意CORBA+TAO的应用,Server端的信息发送(下行通道)和信息接收(上行通道)是在同一个端口进行的,
如果通道被占用,CORBA+TAO的默认方式是等待直到通道被释放。
实际上,这就相当于对于一个共享资源加上锁。

大家都知道,并发程序在使用1个以上的锁的时候,要注意各个线程(进程)必须以相同的顺序来加锁,解锁,否则非常容易造成死锁。

这样,如果在Server端对某些各个客户端的共享数据进行访问的时候,
为了进行并发处理控制而加上锁,就很容易造成意想不到的死锁问题。
原因就是没有意识到CORBA+TAO的通信通道也存在一个锁,
如果使用的这些锁的顺序没有保持一致,就会造成死锁。

我们开发了1个分布式Trap处理系统。
1台PC上Trap处理的Server程序作为Service在开机后被启动,一直在运行,在指定的端口上监听Trap信息。
各个需要处理Trap信息的应用程序,可以发送连接请求到Server上请求连接。
也可以在不需要处理Trap信息的时候,发送断开连接请求到Server上请求断开连接。
Server上保留连接成功的各个Client端的Stub。

各台打印机上设置产生Trap的时候,信息发送的目标地址和端口为上述Trap处理Server的IP地址和端口。
这样各台打印机上由于各种错误(缺纸,卡纸,缺墨,等等)触发Trap信息的时候,
这些Trap信息被发送到我们的Server的端口上,被我们的Server处理程序捕捉,
Server处理程序会通过CORBA来通知各个成功连接的客户端。

在Server端保存了一个成功连接的客户端的队列。
需要处理Trap信息的应用程序,发送连接请求到Server上,Server端会往这个队列里面加上此客户端的Stub;
不需要处理Trap信息的时候,客户端应用程序发送断开连接请求到Server上,Server端会从这个队列里面删除此客户端的Stub;
此外,Server端的接受Trap信息的线程会访问这个队列,取出里面的每一个客户端的Stub,向这个客户端发送Trap通知。
上述的所有针对客户端队列的操作,都需要加上锁来进行并发控制。

最开始的时候,我们设计的处理流程如下:
Server端接收到客户端的连接请求的时候(线程1
1)客户端发送请求到Server端的CORBA端口
2)使用的CORBA中间件自己进行一些处理
3)Lock客户端队列的访问锁
4)检查此客户端是否已经连结过,如果没有,把此客户端的Stub加入到此客户端队列
5)Unlock客户端队列的访问锁
6) 使用的CORBA中间件自己的一些处理
7)返回请求成功给客户端

Server端接收到Trap的时候(线程2)
1)Lock客户端队列的访问锁
2)取得队列中每一个客户端的Stub
3)发送Trap通知信息到Client端的CORBA端口
4)Client端处理此Trap通知信息
5)处理完毕,从Client端返回
6)Unlock客户端队列的访问锁

以上的处理,看上去没有任何问题,
但是我们在进行多个客户端反复请求连接,请求断开连接 + 产生大量Trap通知
这样的压力测试的时候,就发现时间不长,Server程序就停止处理了,
经过检查Log,确定是发生了死锁。

原因就是CORBA+TAO的通信通道存在另外一个锁,
和我们自己加上的锁,2个锁的加锁顺序在多个线程中不一致。
可以参考下面的分析,CORBA的通信通道锁用蓝色字体表示。

Server端接收到客户端的连接请求的时候(线程1)
1)客户端发送请求到Server端的CORBA端口
a)Lock CORBA的通信通道锁
2)使用的CORBA中间件自己进行一些处理
3)Lock客户端队列的访问锁
4)检查此客户端是否已经连结过,如果没有,把此客户端的Stub加入到此客户端队列
5)UnLock客户端队列的访问锁
6) 使用的CORBA中间件自己的一些处理
7)返回请求成功给客户端
b)Unlock CORBA的通信通道锁

Server端接收到Trap的时候(线程2)
1)Lock客户端队列的访问锁
2)取得队列中每一个客户端的Stub
a)Lock CORBA的通信通道锁
3)发送Trap通知信息到Client端的CORBA端口
4)Client端处理此Trap通知信息
5)处理完毕,从Client端返回
b)Unlock CORBA的通信通道锁
6)UnLock客户端队列的访问锁

可以看到,
线程1加锁的顺序是先CORBA的通信通道锁客户端队列的访问锁;
线程2加锁的顺序是先客户端队列的访问锁CORBA的通信通道锁;
2个线程中的加锁顺序不一致,从而导致产生死锁现象。

解决问题的方法是,
线程1的处理的第3步,进行Lock的时候,
使用Lock失败即返回的方式进行调用,
Lock失败后就作为无法连接返回。
客户端得到此错误,等待一定时间进行Retry。
这样就可以避免死锁问题。也不影响程序正常运行。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值