我现在遇到一个问题,我现在在做一个餐饮的服务端,因为使用的是ID卡,所以每次查询余额或者消费的时候都要去连接数据库查询,因为会存在并发处理,比如一个消费事件对上下面N台餐饮机同时触发,所以每次在查询余额或者消费的时候,都是创建一个单独的ADO连接数据库,查询数据或者插入数据这样的操作之后,在事件结束的时候把创建的ADO,free掉,但是现在有个问题不知道怎么解决,就是我这个服务端平时用着都好好的,但是比如用过一周或者几天后,就会自动退出,无报错的自动退出,异常捕获也捕获不到什么,重新打开服务端的话,又可以用几天或者一周,然后再无故退出,不知道各位大大有没有什么好的建议或者有没有遇到类似的情况

解决方案 »

  1.   

    LZ,你的问题可以说每个人都遇到了,这个一般都是非法访问,或者内存溢出造成的。看来LZ需要调试一段时间了。上个月,我被客户叫道现场解决,通过砍代码,分析内存泄露,写Log最终终于解决了。LZ加油。一般这个问题,外人给发给你一个确切的答案
      

  2.   

    代码大概是这样的:
    Try
          //'Provider=SQLOLEDB.1;Password=sanxingxtep;Persist Security Info=True;User ID=sa;Initial Catalog=NT_COWA_XTEP2010;Data Source=192.168.3.44'
          ADOConnect := TADOConnection.Create(nil);
          ADOConnect.LoginPrompt := False;
          ADOConnect.ConnectionString := 'Provider=SQLOLEDB.1;Password=zly19811205;Persist Security Info=True;User ID=sa;Initial Catalog=NT_COWA_XTEP2010;Data Source=192.168.0.97';
          ADOConnect.CommandTimeout := 1800;
          ADOConnect.Connected := True;
          ADO_Select := TADOQuery.Create(nil);
          ADO_Select.Connection := ADOConnect;
        except
          on e:exception do
          Begin
            StrExp := FormatDateTime('yyyy-mm-dd hh:mm:ss',now())+' ADOConnect :'+e.Message+'**ErrorCode:36';
            UMainServe.FMainServer.WriteException(StrExp);
            CanContinue := False;
            ErrorCode :=36;
            Exit;
          End;
        End;
        try
          //消费事件语句
        finally
          ADO_Select.Close;
          ADO_Select.Free;
          ADO_Select.Destroy;
          ADOConnect.Connected := False;
          ADOConnect.Free;
        end;
      

  3.   

    消费事件中,ado_select执行了6次的与数据库的交互,其中5次查询一次插入
      

  4.   

    1 可能的话 ADOConnect和adoquery改为一次性申请,修改状态就行了,不要总是新创建
    2 另外建议Lz在try中间加入日志处理,把错误记下来便于差错,还有,别随便只记错误信息,把当前一些控件状态也记下来
    3 Lz没有发现那两个创建和释放有点问题么,第一个try except包括了创建,但是如果创建意外,却没有进行处理,反而是在第二个try finally中进行释放,那么假设第一个 try发生意外,程序可不是直接崩了
      

  5.   

    谢谢你的回复,可当第一个try意外的时候,不是直接exit出去了么,还会执行到下面的try么?
    我有尝试过使用一次性申请的,但是是有多台设备同时触发这个事件操作的,如果用单个ADO,我怕在并发下会报错或者延迟什么的,所以才一直创建新的,还有,您说当前一些控件状态,能告诉我,最好都记忆什么状态么,而且我在程序退出后,要去查错误日志的时候,发现没这个文本,也就是说没采集到
      

  6.   

    LZ没明白我的意思
    我是说比如
    adoconnection或者TADOQuery创建或者链接发生意外,第一个try退出
    当执行到你第二个try时,尝试关闭和释放一个空的connection或者adoquery肯定会意外吧,最要命的是,这个意外还是在finally里抛出的,程序可不直接崩溃了么?
      

  7.   

    如果并发处理可以使用数据库的事物啊transaction,没必要非创建多个的,另外即便是要创建多个,adoconnection一个就够了,query可以多个
    还有我所说的状态就是看看你创建的adoconnection和adoquery是否正常链接,是否为空,或者adoqueyr上一次执行的sql语句是什么(可能执行中报错的)等等
      

  8.   

    8L说错了,更正,当你的adoquery和adoconnection创建失败时,没有对对象进行可能的清理,这样就留下了内存溢出的隐患
      

  9.   

    我知道,但是我的疑惑是,我在第一个try  except  end  的except  写了exit退出了,他不是不会再执行到下面的语句直接退出该事件了么,如果是在第二个try里发生了意外,比如查询出错或者插入出错的时候,那个ado还是存在的不是么
      

  10.   


    我想问下,比如我在程序刚创建的时候,创建了一个adoconnection,然后在消费事件上只创建adoquery,然后该adoquery连接最初创建的adoconnection,这样可以么,当并发的时候,会不会堵塞什么的,就是比如我两个adoquery同时查询的时候,adoconnection是会排先后还是报错,并发数有可能有40多个或者以上的
      

  11.   

    先回复11L的,刚才是我粗心,没仔细看里面还有个exit;光看你的释放和创建了
    实际上应该用一个try exception 包起来,意外时加个状态判断就可以了,为空时不考虑,如果不为空则释放
    如果抱着程序崩溃就无需释放的心理,你会发现你的机器会越来越慢,这也是程序崩溃的隐患之一另外如果adoquery执行失败,有时候从意外看不出原因的,要看里面的sql语句,建议你在执行查询之外的操作时加上事务处理,因为并发操作会有各种意想不到的问题的,你可以查下begintransaction
      

  12.   

    再次谢谢你的回复,您的意思是我的第一个try里应该要加上释放是么,比如  if adoquery <> nil then adoquery.free,这样的?不是的话,创建失败的话,怎么释放呢
      

  13.   

    如果仅仅是查询是没有限制的,但是写入修改必须加入事物处理,这是并发处理数据的基础
    LZ可以看下这个,人家的adoquery和connection可不是每次都创建哦
    http://hi.baidu.com/plovemxz/blog/item/ae5b7e2825dd17f599250a1d.html
      

  14.   

    呵,我没用到线程,回到刚才的疑问,您的意思是我的第一个try里应该要加上释放是么,比如 if adoquery <> nil then adoquery.free,这样的?不是的话,创建失败的话,怎么释放呢
      

  15.   

    还有
    http://topic.csdn.net/t/20020801/07/915061.html
    2L的回复
      

  16.   


    我可以直接在exit前面加一个
    FreeAndNil(ADOConnect);
    FreeAndNil(ADO_Select);
    这样么,不判断是不是Nil的
      

  17.   

    天哪,说了半天LZ都没听明白啊,唉~建议看看我连接里的那本书,判断是要的,free也是要的free和freeandnil的区别
    http://kudick.blog.163.com/blog/static/16660663200931311194482/
      

  18.   

    另外ADO_Select.Close;这句才是空引用报错的元凶啊
      

  19.   

          ADO_Select.Close;
          ADO_Select.Free;
          ADO_Select.Destroy;
    发现问题了么?
          .close为空时会出错需要判断
          .Free;是正确的
          .Destroy;这个则是多余的
      

  20.   


    我知道,正如你所说的,free自身不是就判断是不是nil了么
      

  21.   


    刚才我说的是,能不能直接在上面的那个try的except 中的exit前面加
    FreeAndNil(ADOConnect);
    FreeAndNil(ADO_Select);
    这个
      

  22.   

    楼主的代码写的是有点问题,不过不大可能是因为connection和query引起的问题吧
    下面这句会不会出错呢
    UMainServe.FMainServer.WriteException(StrExp);这个方法如果抛出异常,外层调用对异常有没有处理呢多记录点日志,就能定位问题了
      

  23.   

    回复29L
    也不能这样说,以前我做过一个例子,有时候因为网络或者数据库的原因,连接会超时,报意外,而以下的adoquery则跟着意外,总之日志的习惯是好的
      

  24.   

    原来在win98环境下,碰到过类似的,后来用winxp才解决
      

  25.   

    我只是猜测,具体原因你最好能根据事实找出来,比如通过查日志
    另外,执行sql的时候也有可能报异常,因此下面的代码也应该捕获异常
      

  26.   


    有的,我每次查询都有使用try语句,但是自动退出的时候,都没记录下来
      

  27.   

    正确写法  
     Try
         if not assigned(ADOConnect) then //此处可以做成一个单例
           ADOConnect := TADOConnection.Create(application); //正常下让adoconnect跟着主程序一起释放
         ADOConnect.LoginPrompt := False;
         ADOConnect.ConnectionString := 'Provider=SQLOLEDB.1;Password=zly19811205;Persist Security Info=True;User ID=sa;Initial Catalog=NT_COWA_XTEP2010;Data Source=192.168.0.97';
         ADOConnect.CommandTimeout := 1800;
         ADOConnect.Connected := True;
        except
          on e:exception do
          Begin
            freeandnil(ADOConnect);
            StrExp := FormatDateTime('yyyy-mm-dd hh:mm:ss',now())+' ADOConnect :'+e.Message+'**ErrorCode:36';
            UMainServe.FMainServer.WriteException(StrExp);
            CanContinue := False;
            ErrorCode :=36;
            Exit;
          End;
        End;
        try
          ADO_Select := TADOQuery.Create(nil);
          ADO_Select.Connection := ADOConnect;
          //消费事件语句
        finally
          freeandnil(ADO_Select);
        end;
      

  28.   

    会不是你的数据库连接数超限而导致?ADOConnect即使释放了,连接数还是有的。建议ADOConnect只建一个ADOQuery建立多个。
      

  29.   


    我想问的是,如果只用一个ADOCONNECTION的话,当并发查询的时候,会不会有影响,我是怕这个才创建那么多个的
      

  30.   

    先上个连接池吧,然后加日志慢慢调试,
    运行一段时间后,看一下内存的使用情况,是不是稳定的连续不断的上涨(如果有泄露,一般会把服务器吊死,但自动退出好像不多)
    也可以用fastmm,eurakalog(写错了么?)之类的工具看一下有没有内存泄露另外,如果你的服务端是个一般的exe的话,可以再写个守护程序,发现这个程序退出就自动把它启动起来,就象个病毒似的
      

  31.   

    对于这种情况,我习惯这样来写
    //'Provider=SQLOLEDB.1;Password=sanxingxtep;Persist Security Info=True;User ID=sa;Initial Catalog=NT_COWA_XTEP2010;Data Source=192.168.3.44'
    ADOConnect := TADOConnection.Create(nil);
    ADO_Select := TADOQuery.Create(nil);
    try
    try
    ADOConnect.LoginPrompt := False;
    ADOConnect.ConnectionString := 'Provider=SQLOLEDB.1;Password=zly19811205;Persist Security Info=True;User ID=sa;Initial Catalog=NT_COWA_XTEP2010;Data Source=192.168.0.97';
           ADOConnect.CommandTimeout := 1800;
           ADOConnect.Connected := True;
    ADO_Select.Connection := ADOConnect;
    //消费事件语句
    except
    on e:exception do
           Begin
             StrExp := FormatDateTime('yyyy-mm-dd hh:mm:ss',now())+' ADOConnect :'+e.Message+'**ErrorCode:36';
             UMainServe.FMainServer.WriteException(StrExp);
             CanContinue := False;
             ErrorCode :=36;
             Exit;
           End;
    end;
    finally
    ADO_Select.Close;
    ADO_Select.Free;
    ADOConnect.Connected := False;
    ADOConnect.Free;
    end;对象的创建在try外面
    对象的销毁在finally里面, 确保一定能被销毁
    另外,当一个对象被free掉后,不需要再执行Destroy操作。精简出来的代码结构应该是这样的
    object:= TObject.Create;
    try
    try
    object.....;//使用对象
    except
    //异常时触发的操作
    end;
    finally
    object.free;
    end;
      

  32.   

    应该是某个指针类变量没有 GetMem
      

  33.   

    试一试 uses 加 ShareMem; 可以解决指针错误。
      

  34.   

    对象的创建在try外面
    对象的销毁在finally里面, 确保一定能被销毁
    另外,当一个对象被free掉后,不需要再执行Destroy操作。精简出来的代码结构应该是这样的Delphi(Pascal) code
    object:= TObject.Create;
    try
        try
            object.....;//使用对象
        except
            //异常时触发的操作
        end;
    finally
        object.free;
    end;
    学习了。
      

  35.   

    在客户那边运行的,建议使用EurekaLog来监视/记录运行期的异常日志。
    这个非常好。
      

  36.   

      程序自动退出最常见的就是栈溢出,既然是服务程序就要好好思考写服务程序的样子来。对象不要create和free这样会造成内存碎片,长期这样下去服务程序也会崩溃的,要并发就写线程池和连接对象池吧! 我的服务程序以前也是跟你的一样死在那里不工作,但是没有退出来。后来我开线程来测试运行24小时(一天左右),主要是业务对象就是create和free的原因。后来修改为对象池之后,就没有问题了,自己动手吧!
      

  37.   

    update ,insert 時,數據庫會對操作的數據加鎖。
    需要考慮一下丟失更新的問題。
      

  38.   

    既然每次都是意外的關閉,可以考慮一下在主窗體的close事件里追蹤一下異常呢~~