针对项目中同步队列死锁的解决方案
2014-08-26 16:48
489 查看
好久没有写blog了,一来没时间,二来嘛,懒!(这是主要原因)。今天遇到一个很有意思的问题,目前已经解决了,供有相同困境的同仁参考啊!在维护公司的一个有着三年历史的项目的时候,发现了一个很诡异的现象,每次调用开台、检查客位、档案更新等其他接口的时候,webclient客户端均能正常反应,网络信号较差的时候,设置的网络超时机制也可以恰到好处的起到他的作用。唯独提交菜品调用加单接口的时候,会出现服务器端已经显示加单成功,并且开始调用打印机开始打印账单了,而我们可怜的iPad上面均要死不活的出现着一个警告框:“正在提交中,请稍后。。。”
更糟糕的是,为了防止我在调用提交的时候,客户会操作UI,因此我将调用加单接口放进了主线程中,so,程序和死机了没什么两样,一直会保持这个界面,而食客什么也做不了。。悲催啊。。
刚开始,我以为是警告框的问题在作祟,因此我再次设置了一个计时器,当警告框每节操的停留太久的时候,我会调用方法强制把他请走!但是问题又来了!没了这货,客户可以随意操作UI了,但是这次网络请求的生命周期并没有结束,于是更悲剧的结果诞生了:程序频繁crash掉!
我是这么处理收到服务器反馈的数据的:
问题在哪呢?经过我多次调试,发现了一个很有趣的现象,这个现象特别隐秘,因此很不容易发现。因为调用加单接口的时候,需要首先调用客位状态的接口,当收到客位状态接口返回值为1(占用)的时候,就会立刻发起加单的网络请求。但是此时,调用客位状态接口的request释放了吗?他的生命周期走到头了吗?答案是不一定的,有时候会走完,有时候会走不完,并且走不完的概率大约为1%。测试的GGMM也辛苦了啊,点击100次可能有那么一次出现上面宕机的问题,相当考验人的耐心哦!
OK,既然发现了问题,我想了三种处理方式,目前只是采用了其中的一种,现在经过回归测试,没有再现这个宕机的问题,谢天谢地啊!方式1:修改服务器端的加单接口,将检查客位状态的接口与加单接口整合,这个方法很麻烦,需要服务器端的人配合,但是一劳永逸; 方式二:(我目前采用的)
采取延时操作,当收到客位状态的时候,不是立马发送加单请求,而是停留2秒的时间再发送,经过我的研究,2秒钟足够调用十多次检查客位状态的接口了!因此能够解决我的困境; 方式三:加状态值进行判断,如果检查客位状态的接口回调方法结束之后,设置一个状态为YES,当状态为YES的时候才发起网络请求。此方法有缺陷,因为虽然回调方法走完了,但是request有没有Over我也不知道。但是作为一个备用思路未尝不是一个好的解决方案。
好了。就此停笔。
更糟糕的是,为了防止我在调用提交的时候,客户会操作UI,因此我将调用加单接口放进了主线程中,so,程序和死机了没什么两样,一直会保持这个界面,而食客什么也做不了。。悲催啊。。
刚开始,我以为是警告框的问题在作祟,因此我再次设置了一个计时器,当警告框每节操的停留太久的时候,我会调用方法强制把他请走!但是问题又来了!没了这货,客户可以随意操作UI了,但是这次网络请求的生命周期并没有结束,于是更悲剧的结果诞生了:程序频繁crash掉!
我是这么处理收到服务器反馈的数据的:
-(void)operation:(TCSLServiceBindingOperation *)operation completedWithResponse:(TCSLServiceBindingResponse *)response { if(response.error != NULL) { [self performSelectorOnMainThread:@selector(reTry) withObject:nil waitUntilDone:YES]; }else { [self dismissAlertView]; //[self performSelectorOnMainThread:@selector(dismissAlertView) withObject:nil waitUntilDone:YES]; for (id mine in response.bodyParts) { if([mine isKindOfClass:[TCSLService_TCSLService___RequestResponse class]]) { TCSLService_TCSLService___RequestResponse *res =(TCSLService_TCSLService___RequestResponse *)mine; [iBus ResponseXML:res.AResponseXML]; } } if ([self.delegate respondsToSelector:@selector(netWork b1ba TransOnSuccess)]) { [self.delegate netWorkTransOnSuccess]; } } }回调方法:
// 网络传输成功返回委托实现 - (void) netWorkTransOnSuccess{ switch (Bus_Type) { case BUS_CODE_CHECKUSER: { if (self.business_Request.Sucess) { Bus_Type=BUS_CODE_CHECKTABLE; [self.business_Request CheckOpenTable:edtTableCode.text]; }else{ UIAlertView *alertView = [[UIAlertView alloc]initWithTitle:@"提示" message:self.business_Request.Msg delegate:self cancelButtonTitle:@"确定" otherButtonTitles:nil]; [alertView setTag:-1]; [alertView show]; [alertView release]; } break; } case BUS_CODE_CHECKTABLE: { if (self.business_Request.Sucess) { //如果查询客位状态成功 APDocument *doc = [APDocument documentWithXMLString:self.business_Request.XML]; APElement *root = [doc rootElement]; int tableStatus = [[root valueForAttributeNamed:@"PointStatus"] intValue]; NSString *upDateTime = [root valueForAttributeNamed:@"LastTime"]; [self.tcSystem w_UpdateTime:upDateTime]; //记录上次更新的时间 if (tableStatus==3) { //如果是占用状态 //执行加单操作 if (itemOrder.bLock) { NSString *str=[NSString stringWithFormat:@"当前菜单 %@ 已经提交过,确定要提交该菜单吗?\r\n如果在上次提交时服务器端已经完成加单操作,本次提交以及菜品的更改将不会生效。",itemOrder.OrderNO]; UIAlertView *alterView = [[UIAlertView alloc]initWithTitle:@"警告" message:str delegate:self cancelButtonTitle:@"提交" otherButtonTitles: nil]; alterView.tag = 2; [alterView show]; [alterView release]; }else{ [itemOrder lock]; [self performSelector:@selector(submitOrder) withObject:nil afterDelay:2.0f]; // Bus_Type=BUS_CODE_SUBMIT; // [self.business_Request AddOrder:[xml description]]; } } else{ //如果不是占用状态,则不能进行加单操作 NSString * msg = [self getTableState:tableStatus]; UIAlertView * alertView = [[UIAlertView alloc] initWithTitle:@"抱歉" message:msg delegate:nil cancelButtonTitle:@"我知道了" otherButtonTitles:nil]; [alertView show]; [alertView release]; return; } }else{ //如果接收程序没有回应 NSString *msg = self.business_Request.Msg; //NSString * msg = @"查询客位状态失败!是否再次尝试?"; UIAlertView * alertView = [[UIAlertView alloc] initWithTitle:@"抱歉" message:msg delegate:self cancelButtonTitle:@"重试" otherButtonTitles:@"取消",nil]; alertView.tag=TAG_CHECK_TABLE; [alertView show]; [alertView release]; return; } break; } case BUS_CODE_SUBMIT: { //菜品提交返回成功 [alertMsgView setMessage:self.business_Request.Msg]; if (self.business_Request.Sucess) { [alertMsgView setTag:1]; //保存本次加单的时间 [self.tcSystem w_SubmitTime:[self getCurrentTime]]; } else { [alertMsgView setTag:-1]; } [alertMsgView show]; break; } default: break; } }
问题在哪呢?经过我多次调试,发现了一个很有趣的现象,这个现象特别隐秘,因此很不容易发现。因为调用加单接口的时候,需要首先调用客位状态的接口,当收到客位状态接口返回值为1(占用)的时候,就会立刻发起加单的网络请求。但是此时,调用客位状态接口的request释放了吗?他的生命周期走到头了吗?答案是不一定的,有时候会走完,有时候会走不完,并且走不完的概率大约为1%。测试的GGMM也辛苦了啊,点击100次可能有那么一次出现上面宕机的问题,相当考验人的耐心哦!
OK,既然发现了问题,我想了三种处理方式,目前只是采用了其中的一种,现在经过回归测试,没有再现这个宕机的问题,谢天谢地啊!方式1:修改服务器端的加单接口,将检查客位状态的接口与加单接口整合,这个方法很麻烦,需要服务器端的人配合,但是一劳永逸; 方式二:(我目前采用的)
采取延时操作,当收到客位状态的时候,不是立马发送加单请求,而是停留2秒的时间再发送,经过我的研究,2秒钟足够调用十多次检查客位状态的接口了!因此能够解决我的困境; 方式三:加状态值进行判断,如果检查客位状态的接口回调方法结束之后,设置一个状态为YES,当状态为YES的时候才发起网络请求。此方法有缺陷,因为虽然回调方法走完了,但是request有没有Over我也不知道。但是作为一个备用思路未尝不是一个好的解决方案。
好了。就此停笔。
相关文章推荐
- 多ajax请求的各类解决方案(同步, 队列, cancel请求)的令一种解决方案
- DIOCP开源项目-高效稳定的服务端解决方案(DIOCP + 无锁队列 + ZeroMQ + QWorkers) 出炉了
- 多ajax请求的各类解决方案(同步, 队列, cancel请求)
- 多ajax请求的各类解决方案(同步, 队列, cancel请求)
- DIOCP开源项目-高效稳定的服务端解决方案(DIOCP + 无锁队列 + ZeroMQ + QWorkers)
- 主队列-异步执行;主队列-同步执行(死锁)
- 不同服务器上的Java项目文件同步 解决方案(socket 、http)
- 针对 IDEA 开发工具崩溃 导致的项目工程异常的解决方案
- .NET2.0程序集无法在.net 4.0 中运行的解决方案--针对vsts测试项目
- 主队列中添加的同步操作永远不会被执行,会死锁原因
- 多ajax请求的各类解决方案(同步, 队列, cancel请求)
- 不同服务器上的Java项目文件同步 解决方案(socket 、http)
- 针对vs2013运行项目无法打开.lib文件的解决方案
- 多ajax请求的各类解决方案(同步, 队列, cancel请求)
- 同步队列的死锁问题
- Solr(搜索引擎服务)和MongoDB通过mongodb-connector进行数据同步的解决方案,以及遇到的各种坑的总结(针对solr-5.3.x版本),mongodb和solr实现实时增量索引
- 针对Maven项目不显示Maven Dependencies库的解决方案
- 分布式集群项目中同步DB数据的解决方案之Canal
- 解决方案、项目、程序集、命名空间
- 两台SQL Server数据同步解决方案