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

来源:互联网 发布:黑道圣徒3御姐捏脸数据 编辑:程序博客网 时间:2024/04/29 03:54


好久没有写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我也不知道。但是作为一个备用思路未尝不是一个好的解决方案。

     好了。就此停笔。

0 0
原创粉丝点击