程序高速从TCP接收数据后insert存入SQL Server数据库,导致其窗口不可见(如果当前窗口是其他程序)或不可控(虽可见但鼠标无效窗口象死了一样),怎么办?
自己能想到的办法主要是优化运行的程序,比如把接收数据处理里的dim放到整个form上面,用存储程序存数据(insert写进存储程序) 。但是还是不能解决问题。

解决方案 »

  1.   

    “把接收数据处理里的dim放到整个form上面”是无意义的动作。
    主要问题在于,从TCP读写数据和向数据库读写数据与你的程序位于同一线程中,如果处在读写数据的循环里,就没有机会去处理窗口消息,也就是假死的产生。
    解决方法可以是在读写数据的循环里调用DoEvents,但是会比较严重的影响性能;另一个方法就是向其他语言寻找多线程的支持,或者单独编写另一个程序专门用于读写数据,使用进程间通信技术进行同步。
      

  2.   

    传说中的DoEvents啊~~~~~~~~~~
    不过单单用一个DoEvents还不够,程序虽然可以由用户控制,但是CPU的占用率还是会居高不下!最好使用:Private Declare Sub Sleep Lib "kernel32" Alias "Sleep" (ByVal dwMilliseconds As Long)For i = 10 ^ 6
        Debug.Print i
        Sleep 1
        DoEvents
    Next i
      

  3.   

        Sleep 1 
        DoEvents 
      

  4.   

    配合Sleep降低CPU利用率,俺前几天刚刚用了一次
    从98%降到0%
      

  5.   

    怎么不想想doevents和sleep让性能下降了多少。
      

  6.   

    每一个数据包包含30X30的数据,我原来是在for i=1 to 30的循环里分30次每次insert30个数据进sqlserver,发现假死后改为一次把900个数据送入sqlserver由存储过程进行,但是还是假死。我于是没办法了(写900个变量在VB里赋值和在SQL里接收并insert30次花了我整整2个小时)。
      

  7.   


    不太懂DoEvent。我在每次调用存储过程之后写DoEvents,但是无效,还是假死
      

  8.   

     DoEvents 我一般都是用這個東東
      

  9.   


    查了一下,DoEvents的用途是“转让控制权,以便让操作系统处理其它的事件”,也就是避免整个电脑的假死而并不能避免该程序的假死?还是把控制权从某个Sub或函数让出,避免该程序窗口的假死?
      

  10.   


    1)如果用多进程,是一个进程用winsock接收另一个用存储过程存储吗?
    2)VB6可以用多进程吗?怎么用?
    3)DoEvents在这个问题上能起什么作用?怎么用?
    如能示意性地写一下VB6的代码不胜感激。
      

  11.   

    如果一段程序出现了界面假死的情况,那么肯定说明它是运算量是比较大的
    这里加Sleep 1 运行到何年何月去了啊
      

  12.   

    那叫多线程,线程和进程不同。一个进程可以只包含一个线程,也可以包含多个线程,但是一个线程必须属于一个进程。
    一个线程可以理解为一条单一的代码执行路径,前面的代码没有执行完,后面的代码就不会被执行。
    如果用多线程,就是一个线程管用户界面,一个线程管数据读写,当然可以再细化为一个线程winsock一个线程搞数据库。
    基本上可以认为VB6不能用多线程。
    如果程序假死的瓶颈在于网络数据交换或者执行存储过程而非程序本身的循环,doevents在这里起不到什么作用。你应该做的是考虑使用多个单独进程来执行不同任务。举例来说,写程序netaccess.exe执行网络数据交换;dbaccess.exe执行数据库存储过程,通过命令行参数和额外的文件在这些程序间交换数据,或者通过一些进程间通信技术来在它们之间进行通信。
      

  13.   


    也许可以这样:程序A从winsock里读数据,程序B向sqlserve里存数据,中间我只会用文本文件交换数据。但问题是,A程序存到文本文件一定比存到sqlserver快吗?否则A还是会假死啊。
      

  14.   

    把费时的过程写在一个ActiveX EXE内,再在主EXE里引用.这样的话这个过程是在另一进程里执行,不会影响主EXE.如果过程有多个,就使用ActiveX EXE的"单元模型"线程来搞定它.
      

  15.   

    如果你确定“假死”发生在数据库操作上,那么ADO本身就支持异步操作的:
    http://topic.csdn.net/t/20061205/16/5208363.html
      

  16.   

    又试了一下,不要说写入sqlserver,光是处理从winsock读到的数据就已经假死了(封掉了写sqlserver的语句)。
    多线程不会,而且似乎也不好处理,因为读必须用winsock的DataArrival事件。试了一下19楼lyc59888兄的timer办法,DataArrival事件把读到的数据转到Byte数组(winsock最大一次读取量的10倍,以避免处理不完)中,用timer1去翻译数组并存入sqlserver(我觉得把翻译和存储分到2个timer里可能没有益处)。
    试验了一下,timer1不启动一切良好(大部分情况下DataArrival的buffer都没有满,说明winsock得到的cpu足够),timer1一旦启动则开始半假死(按停止按钮十几秒后起作用),此时DataArrival的buffer经常满(说明cpu已经开始不太够),比原来有改善,但是还是不行。看来需要前面sworddx兄和alifriend兄所说的多线程多进程的办法或myjian说的调用ActiveX的方法,不幸我根本不会。哪位“会者不难”的兄弟能否帮我写一下代码搞定此问题?100分+100人民币?
    注:界面和处理的代码都是现成的,低速率调适正常,只需要解决高速率的问题。
      

  17.   

    怀疑变真了,“假死”至少没有完全发生在数据库操作上。:-P你所谓的高速率是什么情况?
    每秒DataArrival事件的次数多、还是每次收到的数据量大?
    如果DataArrival消息太多,肯定无法将界面操作排入消息队列了。如果可以的话,加大缓冲区大小,设法让发送端将多次的小数据发送合并成一次大数据发送。
    还有(这已经是第几遍说了)加上反馈机制,接收端处理了数据包后反馈给发送端,然后发送端才运行发送下个数据包,这才是从根本上杜绝超量发送引发接受端阻塞问题的方法。
      

  18.   

    给个例子给你这里面的WINSOCK下载过程你可以参考一下,估计还是有些问题....不过了解一下思路吧下载用的那个窗体,放在了另一个单元里,它就是另一个线程了http://www.m5home.com/blog/article.asp?id=65
      

  19.   

    如果有办法的话就用多线程吧~如果不想用就使用Sleep+DoEvents咯Sleep 1
    只延时1毫秒,循环1000次才一秒,况且我看楼主的软件使用对象对这个程序的处理效率估计也不会有太多要求
    再说了,我从来都是用Sleep+DoEvents,也从来没试过电脑没响应,如果用了还没响应的话,估计也是其它代码有问题至于效率,现在的电脑内存都是以GB来计算了,CPU单核的都达到3GB+了,普通使用软件的人也不会打开“任务管理器”去查看程序的内存占用量!
      

  20.   


    1)每个数据包2000个byte。需要处理为30x30个integer数组元素,然后再存入sqlserver(30条)。
    2)所谓高速,我试验的结果是,1秒1个数据包没问题(1次DataArrival),再快就要假死。而实际可能每秒10个DataArrival。
    3)winsock的缓存好像是固定8192吧?不知道怎么加大。
    4)合并小数据当然好,问题是发数据包的是现有设备,无法改其软件。
    5)反馈机制同样好,通信里面也都是这么做,但问题同上没有办法。
      

  21.   


    sleep是哪里的函数?vb6里没有啊。
    处理效率其实要求很高,每秒10个数据包源源不断而来(可能持续几个小时),而程序处理每秒1个以上就要假死。
      

  22.   

    那就把你的WINSOCK接收代码放在另一个线程,然后把数据向一个自定义的缓冲区里扔数据处理的代码放在别的线程里,不要在接收的线程里处理它.这样做的话,无论怎么样,至少数据不会漏收.以前我做过一个鸟语音采集卡,很垃圾....它就是不断地把数据向串口里扔,格式是一个标头+长度+数据.而且是单向通讯,要是我有数据没收到,别想再找它重发.没办法,只能专门用一个线程去接收数据,向我自己建立的一个缓冲区里扔,然后再在另一线程里去处理数据.这个数据主要是WAV,我把它保存下来就行.之前没用这种结构,老丢数据;后来就这样,数据没再丢过了.你可以参考一下.另外,我当时是用C写的DLL里整的线程,现在你可以试试ActiveX EXE里的单元模型线程.
      

  23.   

    加DoeventsInsert 的时候批处理 同时 要异步执行
      

  24.   

    唉!插入多少条数据就一定要跑多少次sql吗?好好优化你的程序,一条sql就可以插入多条数据的。
      

  25.   


    在调用存储过程的语句后加了DoEvents(不知是不是这么加?)
    异步执行的事还望明示.调用存储过程算异步吗?
      

  26.   


    我是用1个存储过程插入30条数据(里面有30条insert命令).
    我只知道用union all可以一条insert插入30条数据,但是好像也并不比30条insert快.
      

  27.   

    批量调用存储过程用 ADODB.Command 对象,1次初始化,n次调回。又:我30楼的提案有没有测试?
    这里我对类似问题有个方案:http://topic.csdn.net/u/20080622/02/ea5452eb-3566-4e11-8b27-2b4b796d22d3.html
      

  28.   


    1)我都是用command。commandtype根据直接还是存储过程而定。
    2)您30楼的提案已经试了,速度高后写文本文件也假死了(更不要说处理和存入sqlserver)。我想硬盘操作总比内存慢,所以仅仅是把byte翻译为string然后写入文本文件就足以假死(还没有作其它处理例如二进制byte转double浮点和int整数等)。
    3)您的http://topic.csdn.net/u/20080622/02/ea5452eb-3566-4e11-8b27-2b4b796d22d3.html也学习过了(多谢您多处帮忙),但我的问题是发端是人家的设备,别人编的接收程序据说可以正常接收处理存储,而我的则假死不行,所以还是只能从自己这里找原因。
      

  29.   

    1.优化网络数据到SQL的代码
    2.不要每来一个请求就执行一次insert,对于SQLServer完全可以缓存到500~1000条再批次执行(多条insert之间使用分号分隔即可)
    3.ADO执行异步执行,所以可以考虑采用异步执行另外,要说的是对于高速运行的网络IO放在UI同一线程当中,很难免除相应的问题,个人觉得有一个可行的办法就是写成ActiveX EXE,UI以外的内部放到一个Modal当中,由Form启动以后再创建出实例,从而使种它以后台线程方式来运行。从中导出一些Event用于与UI(Form)进行一些信息交换
      

  30.   

    建议做成三层的
    客户端只负责把数据传给中间层的COM+服务器,然后由中间层完成数据库的UPDATE,INSERT,SELECT等操作
    这样做即能保证数据的完整性,以免发生以外造成错误数据(因为COM+有事务处理机制)
    还能为客户端分担一部分负担
      

  31.   

    三层不等于效率,多一层就多一层和复杂度,多一层的资源消耗。但是使用进程内COM,并没有太大的影响。
      

  32.   


    程序假死没关系,只要生成文件没有丢包就可以了。
    在我的方案中,反馈机制只是为了防止过量传输导致丢包的问题,既然其它程序可以正常接收,就不需要该机制了。你只要实现方案中的接受端部分,接收程序直接将收到的包按byte类型存入文件,通常硬盘操作总是快于TCP传输的,所以用文件没问题。
    你所谓的“把byte翻译为string”功能应该归在数据处理程序中,尽量保证接收程序的速度。界面设计上,接收程序隐藏不可见,也就不存在什么假死问题了;
    数据处理程序有画面,因为是定时扫描目录的,所以在更新数据库时可以将定时器暂停,这样你只要处理好数据更新部分,界面不存在假死问题;
    两者关联上,数据处理程序通过 Shell 函数启动接收程序;需要关闭时也可创建一个标记文件让接收程序定时扫描,当然也可以用其它方式关闭接收程序。
      

  33.   

    如果每个人都拿着别人的BUG来讨论设计自己的程序,那还有多少意义?做事情首先要分清楚责任在于谁。你不可能因为某某超市卖的东西是变质的,你就要求自己买回来马上吃掉吧?有意义吗?
      

  34.   

    这跟开发语言和OS平台什么关系?
    在TCP/IP协议当中,如果对端没有接收下来数据又如何认识已经发送成功,可以进行后续报文的发送?为什么发送异常不加以处理?我从来没有听说过这样存在的问题叫做是“合理”。在网络传输当中存在这样的问题,难道单依赖于接收端就可以单方面把问题解决掉?那还有谁敢用?一个TCP包,随便中间缺少一个字节,那这个报文还能正常解析?难道中间传输的全是无关紧要的数据?
      

  35.   

    我只碰到过的是由于使用消息通知机制进行网络编程,从而可能会因为某一个数据到达的消息被中途丢弃,而得不到相应事件的响应,这其实是一个程序当中由于多种原因而造成的一个“消息黑洞”,在这种情况下是可以通过弥补,比如主动发起select进行网络数据检索,但是这个仅仅只是响应问题,并不影响数据的完整性。一个基于TCP/IP协议的网络程序,不管出于何种原因,如果不能对数据的完整性进行保障,那是不可以使用的,它不象UDP那样某一帧的错误不对后续帧产生影响,而是一个一错则全错的“流”协议。
      

  36.   


    大家好,我用的vb程序运行起来也很占cpu,有时界面也死掉,但程序是运行的(通过串口,在执行发送数据),当数据发送完,界面才可以操作。上面这位朋友的代码要怎么使用?直接全部cope到程序里吗?在下刚学vb,边用边学
      

  37.   

    谢谢!
    “放到线程当中”,不会啊!如何对放到线程中呢?呵呵,难了不会,会了不难吧现在的问题就是程序通过串口发数据时,太占cpu,我在发送程序中加sleep好吗?    Sleep和DoEvents要配合使用吗?我以前一直单独使用sleep啊
      

  38.   

    那样会降你效率。程序本来就是要让它工作的,又为什么怕它占用CPU呢?放线程里,或者使用异步(实际上也是由后台线程进行处理)只是为了用户界面方便操作,并且不至于“死”得那么难看。
      

  39.   


    com+不懂。可以用vb6?说详细点?
      

  40.   

    COM+也就是COM,就是把你的一些功能写成一个VB的DLL。然后把它注册到COM+服务当中就完事了。之后你就创建一个COM实例来应用。
      

  41.   

    这种有多少人会考虑使用TCP协议?TCP这种面向连接的协议,当对方来不及接收被阻塞的时候,又如何称之为网络通畅?对于发送端而言,又谁那么天才往一个TCP连接上面,本来需要发送16个字节数据,结果只发送成功三个字节,在不进行复位和继续发送的情况下发送下一个包?这是你个人的想当然吧?或者举个例子看看哪家的系统有这种需求?
      

  42.   

    to Tiger_Zhao:
      关于一些没有任何实质性意义的话题,个人不想与你再争吵,你有权力去坚持你个人的观点。只是不希望一些所谓的新人把这些当成一个“典范”。To All:
      我个人并不是什么权威,所以也不希望别人生般硬套去接受或者不接受。只是通过一些讨论,希望大家得到启发,从而通过经验积累,包括试验,以及一些相关的权威性的资料(书上说的不一定都是正确的,加上自己的思考是非常必要的),特别如MSDN以及一些相当经典的材料进行分析。不管是老板,还是客户,甚至是你的上级,在技术领域当中并不能代表权威,但是你需要有足够的理由让他们了解、理解并且接受你的方式或方法或者对于问题的分析。当然讨论的目的并不是说让谁违背着上级、老板或者客户地意愿去做事。只是出于尽可能减少或减轻成本,以及维护量,达到更高的生产效率和系统稳定性。就上面我说的这种情况,如果确实碰到,那么可以通过分析和检验报文的完整性来进行处理,因为人有时候反抗不了就得学会遵从。
      

  43.   

    另,to Tiger_Zhao:
        个例性的偶然性的问题并不具代表性,希望以后跟你讨论问题的时候,举出来的例子是具有代表性的。不管是平常的讨论,还是今后工作中,特别是对待下属的时候,举证的可信服度是非常必要的,否则如果没有什么说服力的话,对个人对大家都不存在好处。
      

  44.   

    根据我对通信的了解(我对GPRS比较熟),tcp由于需要确认,所以其速率或带宽不仅取决于发送端的速率,也受限于中间网络带宽和接收方接收的速率。在我的例子里,由于我未能实现高速接收,所以接收端成为制约整个tcp传输的瓶颈。我试了几个不同版本的发送端程序,其中有一个版本就断掉了,估计是tcp发送速率过慢引起(接收方响应太慢)。
    我对winsock的机制不太了解,我理解应该是DataArrival结束后winsock会自动发响应给另一端。结果由于我的DataArrival处理过慢,反馈的响应也就过慢,导致发端因等候响应而数据发不出来而失败。
    用通信的术语讲,tcp的send window不知是多大?window固定为1还是可调?
      

  45.   

    DataArrival并不会产生接收,所以也不会对对端进行响应(当然如果使用的不是WinSock.ocx,那就另当别论),DataArrival只是起一个通知接收的功能,真正产生影响的是你实际做的接收。TCP的Send Window是可调的,所以,当你延缓了接收,可能造成对端减小窗口,甚至是暂停发送,从而减缓了传输的速度,当你恢复接收之后,窗口又会慢慢被调大。所以对于网络程序,更多的建议是通讯和业务相互独立,即当你接收到数据之后在一定的量上进行缓存,另有业务线程进行处理,只有堆满一定量仍然处理不及时,才减缓或者暂停接收,而在这一过程当中尽可能保证速度上不受到过度的制约而导致窗口大小的改变。
      

  46.   

    机制方面不熟悉,要请教unsigned了。只看到MSComm控件有属性设置速率,Winsock的确没有。
    你试试在调用GetData后直接抛弃数据不做任何处理,这样是否能响应最高速率的传输。
    如果还断掉,在找不到设置传输速率的情况下,只能考虑换控件了。
    如果不会断掉,那么考虑将几次GetData()得到的数据缓存起来,积累到一定量后再外送。还有最好用高精度计数器统计一下,各部分的耗时多少,便于针对瓶颈优化。
      

  47.   


    直接抛弃没有问题。而且我做了一个10倍于8192的缓冲数组,把收到的数据拷贝到缓冲数组中,也没有问题。
    没有问题的具体含义是:数据包长2000,DataArrival里的BytesTotal有2000的也有4000的,但是没有更多,也就是不会发生比较复杂的粘连。积累几次GetData再处理是个好主意,起码对于我这样没有更高级手段(例如多线程)的人来说是这样的,只是这样需要太多的变量(一次就需要30x30,10次岂不是30x30x10,近万个变量不仅骇人听闻,而且就怕处理起来还是超时)。我有时间试一下吧。高精度计数器我不懂。瓶颈呢,以前以为主要是写sqlserver,后来发现光是转换数据也不够时间,所以瓶颈应该既包括转换数据也包括sqlserver。具体点说呢,转换数据用到了CopyMemory,sqlserver用了union,CopyMemory听说不是很快,union倒是1条顶30条insert。另外,老兄的写硬盘文件的方法,最开始我自己也曾想过,但是一来这么高速率怕很难实时处理,二来说实话觉得用文件而不是更高级的办法看上去有点土(我自己土,没有得罪老兄的意思)。
      

  48.   

    tiger_zhao兄说的精确测量耗时的想法,说实话我没有概念。我主观瞎猜是硬盘读写比较慢、外部函数比较慢(例如CopyMemory),另外sqlserver肯定就更慢了(数据量大了以后)。
    sqlserver方面,insert估计慢也慢不到哪儿去,而查询就难说了30x200x500=三百万条记录,select起来怎么也是10秒级的,所以到那时候我的绘图程序还会出问题,那将是我的下一个大难题。由于不会在程序之间传递变量,马上就又想到了tiger_zhao兄说的硬盘文件的办法。
    唉,走一步说一步吧。
      

  49.   

    多线程的办法我肤浅地想了一下,有2个方案。
    1)DataArrival收到数据后,调用线程A翻译并存入sqlserver。
    2)DataArrival收到数据后,调用线程A翻译,调用线程B存sqlserver。两个方案,1)比较简单,2)还要协调A和B(例如设翻译处理标志等)。问题是:
    多线程可以避免界面假死?
    多线程可以抢占更多的cpu?
    不管几个线程,如果处理速度还是跟不上接受速度,缓存再大也是要满的(高速连续几个小时),最后岂不是tcp还是要因阻塞而断掉?
      

  50.   

    在VB当中使用CopyMemory并不怎么建议,对于字符串的拼接,建议使用Join,而不是什么CopyMemory或者str = str1 & str2来进行拼接。
      

  51.   


    CopyMemory用于把收到的二进制byte转换为double浮点数,好象VB里没有别的更好的办法。字符串当然不用,integer整数也没有用。
      

  52.   

    从tcp收到的数据都是二进制的byte。下面是DataArrival里的代码(截取各个byte并翻译为integer):Private Sub Winsock1_DataArrival(ByVal bytesTotal As Long)
    On Error Resume Next
    Debug.Print bytesTotal
    Winsock1.GetData DBdata(), vbByte + vbArrayDBid = DBdata(2)        '第2个byte表示消息类型
    If DBid = 4 Then        '4代表数据
        For ccc = 0 To 29   '每个数据包30个数据点,每个数据点有5+30个integer字段
            bpdp = 88        'bytes per data package
            DBcc(ccc) = CInt("&H" & DBdataStr(7 + ccc * bpdp) & DBdataStr(6 + ccc * bpdp) & DBdataStr(5 + ccc * bpdp) & DBdataStr(4 + ccc * bpdp))
            DBsf(1, ccc) = CInt("&H" & DBdataStr(11 + ccc * bpdp) & DBdataStr(10 + ccc * bpdp) & DBdataStr(9 + ccc * bpdp) & DBdataStr(8 + ccc * bpdp))
            DBsf(2, ccc) = CInt("&H" & DBdataStr(15 + ccc * bpdp) & DBdataStr(14 + ccc * bpdp) & DBdataStr(13 + ccc * bpdp) & DBdataStr(12 + ccc * bpdp))
            DBsf(3, ccc) = CInt("&H" & DBdataStr(19 + ccc * bpdp) & DBdataStr(18 + ccc * bpdp) & DBdataStr(17 + ccc * bpdp) & DBdataStr(16 + ccc * bpdp))
            DBsf(4, ccc) = CInt("&H" & DBdataStr(23 + ccc * bpdp) & DBdataStr(22 + ccc * bpdp)) '& DBdataStr(21 + ccc * bpdp) & DBdataStr(20 + ccc * bpdp)) 
            For hhh = 0 To 29   '每个数据点30个字段
                DBchData(hhh + 1, ccc) = CInt("&H" & DBdataStr(23 + ccc * bpdp + hhh * 2) & DBdataStr(22 + ccc * bpdp + hhh * 2))
            Next hhh
        Next ccc
        With cmd
            .CommandText = "dbpro_data_in"               '该存储过程将30条数据insert进sqlserver
            .CommandType = adCmdStoredProc
            .CommandTimeout = 0
            For i = 0 To 19
                ii = Right("0" & Trim(Str(i + 1)), 2)
                .Parameters("@cc" & ii) = DBcc(i)
                .Parameters("@sf" & ii & "01") = DBsf(1, i)
                .Parameters("@sf" & ii & "02") = DBsf(2, i)
                .Parameters("@sf" & ii & "03") = DBsf(3, i)
                .Parameters("@sf" & ii & "04") = DBsf(4, i)
                For j = 1 To 40
                    jj = Right("0" & Trim(Str(j)), 2)
                    .Parameters("@d" & ii & jj) = DBchData(j, i)
                Next j
            Next i
            .Execute
            DoEvents
        End With
    End If
    End Sub注:CopyMemory这里没有,另一个一次性的消息里用(1小时1次所以速度无所谓),这里都是2字节或4字节的integer
      

  53.   

    另外上面的19应为29。
    总之很简单,按规定一段一段截二进制byte并转换为int(低位放前面高位放后面),然后调用存储过程。原来是一条insert一次,后来怕慢就30条insert一次(存储过程里用了insert select union)。
      

  54.   

    你这里面不就有很多的字符串拼接?实话说,这代码确实有点乱。DBdataStr是怎么写的?
      

  55.   


    是啊,这代码不仅乱而且不好。开始是想用16进制显示收到的代码,所以
    dim DBb as long
    DBb = DBdata(i)
    DBdataStr(i)=IIf(Len(CStr(Hex(DBb))) = 1, "0", "") & CStr(Hex(DBb))后来发现速度不行,也没有去再改。是不是可以用CopyMemory直接翻译integer要快些?
      

  56.   

    使用CopyMemory不只是快一小点点。
    你要做的不过是把4个Byte转为long
    而如果使用CopyMemory的话,你可以一次性把N*4个Byte复制到N个Long当中。
      

  57.   

    这个压力基本上不算太大。代码优化还是很有必要的。至于是否使用线程去处理,那可以另外考虑。对于数据库操作,则可以置为异步的,然后多建几个Command(最好独立的Connection),进行池化,应该可以解决大半的性能问题。
      

  58.   


    数据库如何异步?
    多建command相当于多进程?
      

  59.   

    不是多个Command相当于多进程,而是采用异常执行,实际上它就有一个后台线程帮你执行,前端无须等待其完成,你只需要在它的完成事件当中处理。
      

  60.   

    请直接对 DBData() 的内容解释一下字段构成。
    看你似乎先将 DBData 转换为 String 类型的 DBdataStr(),再逐个转化为 Integer 值,完全没有必要;定义好正确的 Type 后用 CopyMemory 就可以解决的。
      

  61.   


    高精度计时器如下
    Private Declare Function QueryPerformanceCounter Lib "kernel32" (lpPerformanceCount As Currency) As Long
    Private Declare Function QueryPerformanceFrequency Lib "kernel32" (lpFrequency As Currency) As Longprivate m_Frequency as currency
    private m_aExpend(1 to 2) as currency
    private sub form_load()
        QueryPerformanceFrequency curFrequency '这是个定值,只需要初始化一次
    end subprivate sub form_unload(...)
        dim i as long
        for i=1 to 2
            Debug.Print "代码段" & i & "总耗时 " & FormatNumber(m_aExpend(i)/m_Frequency, 7) & " 秒"
        next
    end subprivate sub Winsock1_DataArrival(...)
        dim curStart as currency, curFinish as currency
        '代码段1
        QueryPerformanceCounter curStart
        ...
        QueryPerformanceCounter curFinish
        m_aExpend(1) = m_aExpend(1) + (curFinish-curStart)
        '代码段2
        QueryPerformanceCounter curStart
        ...
        QueryPerformanceCounter curFinish
        m_aExpend(2) = m_aExpend(2) + (curFinish-curStart)
    end sub
      

  62.   

    上面 curFrequency  更正为 m_Frequency
      

  63.   

    79楼代码中,改为2个CopyMemory一次性转换4+30个integer后,如果不存数据,数据接收正常,数据包会有2-3个的粘连,发送方程序始终在发送;而一旦存数据,则数据接收不正常,发生4个以上的粘连,在接收2个数据包后(每个30条),发送方程序发送2个数据包后结束发送(应该是认为接收方处理能力不够)。具体来说,在copyMemery之后:
    1)封掉with cmd里所有命令,正常。
    2)执行with cmd里的所有命令,不正常。
    3)封掉后面的存储过程调用,不正常。(在with cmd里仅封掉.execute)看来问题在于对存储过程变量赋值和调用存储,而这两个显然缺一不可,而且我也想不出好的解决办法。
    我也试了直接用command单条存储,同样不行。
      

  64.   


    能想到的优化已经作了:2条CopyMemory替代了30+4条转换命令(不再事先转换为string)。
    多线程我觉得并不一定能解决问题,异步数据存储也不行,因为再多的线程和异步如果不能及时处理数据,都会产生接收端的数据淤积,并最终导致发送方tcp窗口减到最小而导致数据传送失败。
    异步还有一个问题,就是另外还有程序会读取sqlserver而实时显示数据,异步时延过大会使实时显示无法进行。
      

  65.   

    对于这样的程序,在VB当中要想找一个简单的处理方式,几乎不可能。
    如果存储过程当中并没有太过复杂的处理,仅仅只是一个Insert的话,个人并不建议使用存储过程。对端之所以断掉,估计应该是发送超时。对于异步处理,跟显示数据本身并没有任何关联性的影响,之所以会有影响,是因为你使用的数据库锁,或者说连接上面的隔离级别不对。比如发送端,如果你使用的是with (nolock)或者with (readpast)那么就不会有影响。
      

  66.   


    1)使用存储过程是听说比较快。不过也可以试一下直接用insert select union all。
    2)异步的事我始终没有搞清楚。存储过程是异步吗?用DoEvents是异步吗?
    3)显示的问题和异步的关系在于,如果异步存储的是10分钟以前的数据(由于处理速度不够),那么我们不可能显示最近10分钟的数据,所以不能做到真的实时显示。
    4)我想大概可以用超大数组,这样把所有收到的数据放入数组,起码不会丢数据。而且已经试验过存数组的速度是没有问题的。据说数据不能超过long的范围也就是2,147,483,647,这对于我估计的2百万最大数据条数应该够了,只是2M x 40个integer应可能超过计算机2G内存(数组会先存入内存?),超过后操作系统会用硬盘替代?效率可能也不行。
      

  67.   

    1) 使用存储过程不一定快,要看是在什么情况下使用。insert并不一定要使用union all,但是建议使用批量提交的同时采用显示事务,即在你的insert里句前后带上begin transaction/commit transaction(可能还会有rollback transaction)
    2) 存储过程并不是异步,存储过程只是把一大批的频繁使用的命令集中在一起,并且由于存储过程在数据库当中会进行Compile,所以会有一定的性能提升,但并不是建议在这些insert之类的简单命令的基础上的,比如使用游标进行批量数据的处理等等;DoEvents也不是异步,DoEvents只是为了使得当前线程能够响应Windows消息,从而插入一些临时的消息处理,而使得一些基于消息的动作得到处理,包括窗口上面的刷新,以及平常的鼠标拖动等等,如果懂得中断概念的话,那么这个就属于“中断”,如果有消息投到消息队列当中的话.
    3) 数据没有入库,那明显无法进行显示,除非直接投到显示窗口当中,而不是数据库中转,但是这个可能就不再符合设计,甚至只能是想想罢了
    4) 使用内存进行缓存,那只是暂时的,通常用于处理那些,间歇性集中流量的数据,比如某一分钟可能突然要传好几百条数据,而后可能会歇个两三分钟再来一批,对于一直连续不断送过来的数据,通过一定的时间,如果不能及时处理,只能使得缓存越来越大,最终整个系统由于内存的过度消耗而越加缓慢,越加堆集,而陷入死循环,好一点的情况是对端中断连接,坏一点的情况是整个系统挂起不再工作。
    我们在考虑这些问题的时候,需要集中在:
      a. CPU的处理能力是否已经完全达到利用
      b. 业务流程各个环节还能不能进行进一步的性能改善,包括流程的改善以及编码效率提升
      

  68.   


    1)我只知道insert union all可以一次插入多条数据。而如果一次一条速度当然就慢了。
    2)事务的好处在这里例子里没看到,我不需要回滚,反而觉得事务会降低存储速度。
    3)还是不知道异步具体该怎么做。
    4)我如果能把所有收到的数据都放入物理内存(假设2G够用),那么系统也会缓慢吗?
    5)提高CPU利用率有什么办法?多线程可以吗?
    6)我的业务流程很简单,收到数据>翻译>入库,好像看不到余地了(除了全部存入数组然后慢慢入库)。
      

  69.   

    用insert union all出了问题,VB说我“行太长”,我还只写了一条数据的插入:
        cmd.CommandText = "insert into rdata" & _
            "select 0," & DBcc(0) & "," & DBsf(1, 0) & "," & DBsf(2, 0) & "," & DBsf(3, 0) & "," & DBsf(4, 0) & "," & DBchData(1, 0) & "," & DBchData(2, 0) & "," & DBchData(3, 0) & "," & DBchData(4, 0) & "," & DBchData(5, 0) & "," & DBchData(6, 0) & "," & DBchData(7, 0) & "," & DBchData(8, 0) & "," & DBchData(9, 0) & "," & DBchData(10, 0) & "," & DBchData(11, 0) & "," & DBchData(12, 0) & "," & DBchData(13, 0) & "," & DBchData(14, 0) & "," & DBchData(15, 0) & "," & DBchData(16, 0) & "," & DBchData(17, 0) & "," & DBchData(18, 0) & "," & DBchData(19, 0) & "," & DBchData(20, 0) & "," & DBchData(21, 0) & "," & DBchData(22, 0) & "," & DBchData(23, 0) & "," & DBchData(24, 0) & "," & DBchData(25, 0) & "," & DBchData(26, 0) & "," & DBchData(27, 0) & "," & DBchData(28, 0) & "," & DBchData(29, 0) & "," & DBchData(30, 0)要是写30条岂不是长死了,所以就根本没有想把DBchData改名为Dch之类的办法。看来还是只能用存储程序把900个变量穿过去然后在存储程序里写insert union all。但是对900个变量赋值这部分时间又受不了。好像除了大内存就没路了。
      

  70.   

    \
    1) insert ...data1...;insert ...data2...;insert ...data3...;insert ...datan...,这样就可以替代union all,一般来讲一次可以提交500~1000笔,从而减少与数据库的交互(网络)次数,以提升效率
    2) 事务是肯定存在的,只要对数据进行过修改(包括新增和删除)。只是如果你没有使用显式事务,则由系统来执行事务策略,即隐式事务。
    3) 实际上如果只是insert,并且数据库当中的索引使用合理的话,那么基本上不需要异步也不需要多少时间,如果你要进行异步执行,那么就首先需要建立数据环境池(多个连接和Command组成,当其中一些被占用的时候,其他空闲的可以顶上来被使用,使用数组就可以完成),当然每次都申请新的是可以,但是频繁创建和销毁对象也是需要消耗时间的.ADODB.Command.Execute的第三个参数,可以带上adAsyncExecute,即为异步执行,但是需要注意ADODB.Command的一些相关事件需要进行处理
    4) 2G内存并不算多,即使你有超过2G以上的内存,试问一下,VB6能用到多少内存?超不过2G,再说能缓存的数据量也是有限的,并且就象你把手头上忙不完的工作堆积下来,没啥区别
    5) 提升CPU的利用率,并不一定要使用多线程,除非你的机器是多CPU(包括多核心),我的意思是先要看一下你当前的CPU压力能达到什么程度,如果满负荷(单核心当中,比如你的CPU是双核的,那么只需要占用到总的50%就算),那么你操作数据库是否需要使用异步并不太重要。
    6) 你所给出的业务流程只是一个大概的,每一个分步当中还包含很多细节,比如说通过内存占用来达到性能提升,比如说将数据进行短时间缓存然后一次性提交等等
      

  71.   

    我在前面的回贴当中了,少用 & 来进行字符串连接,而使用字符串数组进行数据的组织,然后再使用join进行拼接