针对项目中同步队列死锁的解决方案

在维护一个有着三年历史的项目时,遇到了在iPad上使用警告框导致网络请求生命周期未结束的问题。通过设置延时操作和状态判断,最终解决了在提交菜品时iPad UI与后台网络请求之间的冲突。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


好久没有写blog了,一来没时间,二来嘛,懒!(这是主要原因)。今天遇到一个很有意思的问题,目前已经解决了,供有相同困境的同仁参考啊!在维护公司的一个有着三年历史的项目的时候,发现了一个很诡异的现象,每次调用开台、检查客位、档案更新等其他接口的时候,webclient客户端均能正常反应,网络信号较差的时候,设置的网络超时机制也可以恰到好处的起到他的作用。唯独提交菜品调用加单接口的时候,会出现服务器端已经显示加单成功,并且开始调用打印机开始打印账单了,而我们可怜的iPad上面均要死不活的出现着一个警告框:“正在提交中,请稍后。。。”

更糟糕的是,为了防止我在调用提交的时候,客户会操作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(netWorkTransOnSuccess)]) {
            [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我也不知道。但是作为一个备用思路未尝不是一个好的解决方案。

     好了。就此停笔。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值