为什么这段代码非常耗费cpu资源?
请帮忙看看,为什么用winsock下面的代码接受并存储数据时,cpu占用率到了80%,甚至90%呢?哪些地方耗费资源呢?真奇怪啊,下载数据的时候,cpu占用的太厉害了 -------------------------------------------------------lightwind 写的是支持多线程(多winsock同时下载的代码)。调用方法:在data_Arrival()中 
        Dim DataByte() As Byte  '用字节数组接受数据
        socks.GetData DataByte
        If SaveData(DataByte(), "wizenflower.gif", 0) = False Then '调用一个winsock(0)
           ...
        End If        If SaveData(DataByte(), "wizenflower.gif", 1) = False Then '再调用一个winsock(1)
           ...
        End If完整代码如下:
'这是一个支持从网站以HTTP协议下载的模块,支持多线程(WINSOCK)模式。Private Arrival_First_Times() As Boolean    '数据第一次到达
Private F_Num() As Integer '取得系统的空闲文件号
Public File_Lenth() As String '待下载的文件大小
Public Received_Byte() As Long '已接受字节Public Function SaveData(DataByte() As Byte, FileName As String, Pros_ID As Integer) As Boolean
'DataByte():接收到的字节数组,FileName:要保存的文件名ros_ID:线程ID,支持多WINSOCK。'根据线程数定义动态数组的大小(保护原有数据模式)
ReDim Preserve Arrival_First_Times(Pros_ID + 1)
ReDim Preserve F_Num(Pros_ID + 1)
ReDim Preserve File_Lenth(Pros_ID + 1)
ReDim Preserve Received_Byte(Pros_ID + 1)On Error GoTo err
If Arrival_First_Times(Pros_ID) = False Then '数据第一次到达的时候,包含消息头,也可能一起包括2进制文件:所以检查消息头,并分离数据        Dim DataStr As String
        DataStr = StrConv(DataByte(), vbUnicode) '这里是将字节数组转化为字符串,以便于操作
    
        If InStr(1, DataStr, "HTTP/1.1 200 OK") Or InStr(1, DataStr, "HTTP/1.0 200 OK") Then '检查http响应状态
             '取得待下载的文件大小字节数
             Dim pos1, pos2, Title_Lenth, Start_Pos
             Title_Lenth = Len("Content-Length:")
             pos1 = InStr(1, DataStr, "Content-Length:")
             pos2 = InStr(pos1, DataStr, vbCrLf)
             File_Lenth(Pros_ID) = Mid(DataStr, pos1 + Title_Lenth, pos2 - pos1 - Title_Lenth)
             
            '从服务器返回的数据中,取得下载文件的起始位置。
            '说明:消息结束和文件结束的地方,分别用2个换行表示数据结束。一个换行vbcrlf占用2字节。
            '在字符串中,2个换行就是2个vbcrlf;而在字节数组中,就是连续的DataByte(i)=13,DataByte(i+1)=10,DataByte(i+2)=13,DataByte(i+3)=10
            
             For i = 0 To UBound(DataByte()) - 3 '减3是因为文件的最后也是2个换行,而我们需要的是消息头和文件之间的分割,所以不搜索最后的那2次换行。
                 If DataByte(i) = 13 And DataByte(i + 1) = 10 And DataByte(i + 2) = 13 And DataByte(i + 3) = 10 Then
                 Start_Pos = i + 4 '加4是因为每个换行占用了2个字节,所以往后4个字节,正好是二进制数据开始的地方
                 Exit For
                 End If
             Next
             
             '将第一次接受到的字节,除去HTTP响应标题以外的二进制数据,写入文件
             If Dir(App.Path & "/" & FileName) <> "" Then '第一次写入文件,如果写入之前文件已存在,则删除。
             Kill App.Path & "/" & FileName
             End If
             
             F_Num(Pros_ID) = FreeFile()
             Open App.Path & "/" & FileName For Binary Lock Write As #F_Num(Pros_ID)
                For i = Start_Pos To UBound(DataByte())
                    Put #F_Num(Pros_ID), , DataByte(i)
                Next
             Close #F_Num(Pros_ID)
             
             Arrival_First_Times(Pros_ID) = True '已经完成第一批到达数据的处理。
             
       Else
            SaveData = False
            Exit Function
       End If
       
       Received_Byte(Pros_ID) = Received_Byte(Pros_ID) + UBound(DataByte()) + 1 - Start_Pos
       
Else '如果不是第一到达数据,则直接写入文件        F_Num(Pros_ID) = FreeFile()
        Open App.Path & "/" & FileName For Binary Lock Write As #F_Num(Pros_ID)
        If LOF(F_Num(Pros_ID)) > 0 Then Seek #F_Num(Pros_ID), LOF(F_Num(Pros_ID)) + 1
        Put #F_Num(Pros_ID), , DataByte()
        Close #F_Num(Pros_ID)        Received_Byte(Pros_ID) = Received_Byte(Pros_ID) + UBound(DataByte()) + 1
End IfSaveData = True
Exit Functionerr:
SaveData = FalseEnd Function

解决方案 »

  1.   

    For i = 0 To UBound(DataByte()) - 3 '减3是因为文件的最后也是2个换行,而我们需要的是消息头和文件之间的分割,所以不搜索最后的那2次换行。
                     If DataByte(i) = 13 And DataByte(i + 1) = 10 And DataByte(i + 2) = 13 And DataByte(i + 3) = 10 Then
                     Start_Pos = i + 4 '加4是因为每个换行占用了2个字节,所以往后4个字节,正好是二进制数据开始的地方
                     Exit For
                     End If
                 Next
    =========================================你这段代码不是一般的占资源啊
    你databyte数组那么大,还一个个的循环查找不慢死你才怪
      

  2.   

    而且你还要不断的有数据到达,于是程序不断的for循环。其实vb的for是很慢的。。
      

  3.   

    多谢指点,但是那个是第一次数据来的时候,以后数据走这里:就是这里:Else '如果不是第一到达数据,则直接写入文件        F_Num(Pros_ID) = FreeFile()
            Open App.Path & "/" & FileName For Binary Lock Write As #F_Num(Pros_ID)
            If LOF(F_Num(Pros_ID)) > 0 Then Seek #F_Num(Pros_ID), LOF(F_Num(Pros_ID)) + 1
            Put #F_Num(Pros_ID), , DataByte()
            Close #F_Num(Pros_ID)        Received_Byte(Pros_ID) = Received_Byte(Pros_ID) + UBound(DataByte()) + 1
    End If这段代码估计有非常消耗cpu的问题,是不是seek呢?能否直接追加呢?
    我并没有用多线程,我只用一个线程,也非常慢,cpu占用90%,在下载过程中持续如此。真是奇怪
      

  4.   

    而且接受的数据一般如下:HTTP/1.0 200 OK
    Content-Length: 6813
    Content-Type: image/gif
    Last-Modified: Mon, 04 Dec 2006 10:34:41 GMT
    Accept-Ranges: bytes
    ETag: "32cc86ce8f17c71:2bad"
    Server: Microsoft-IIS/6.0
    X-Powered-By: ASP.NET
    Date: Sun, 07 Jan 2007 09:23:53 GMT
    Connection: close
    后面就是图片数据了,所以循环最多几百次啊。而且仅在第一次数据到达时才循环查找数据开始的标志,就是两个回车,以后就不查询了。
      

  5.   

    要是你能带个控件就好了......就可以用这招:http://community.csdn.net/Expert/topic/5628/5628790.xml?temp=.5483515里面我写的那个例子是同时下载500份网页的.....不过杀毒软件怎么对付....是你的事了....
      

  6.   

    如果你只是解决占用资源问题,那倒是好办,你只要大方点,也时不常的分给系统一点时间。在循环中,每循环一定次数运行一次DOEVENTS。如:
    dim iCunt as integer
    dim boolABC as boolean
    boolABC=false
    iCunt=0
    For i = 0 To UBound(DataByte()) - 3 
       If DataByte(i) = 13 And DataByte(i + 1) = 10 And _
          DataByte(i + 2) = 13 And DataByte(i + 3) = 10 Then
          Start_Pos = i + 4 
          boolABC=true 
          'Exit For
       End If
       if boolABC then exit for     '此处修改你的代码,因为有bug   iCunt =iCunt+1
       if iCunt=32 then
          iCunt=0
          doevents           '把cpu转让给系统一小会
       end if
    Next
      

  7.   

    哦,晕~我大概知道了
    你不要每次数据到达都open文件
    open这个动作是非常慢的,因为每次都要读写磁盘,cpu都要等待磁盘的回应解决的办法是,你一开始open一次,然后文件不要关闭,直到收完才close,你试试看
      

  8.   

    多谢VirtualDesktop,根据您的提示,我去掉了每次的open和seek,效率应该有所提高,但是cpu占用率依然很高,达到60-80%,Else '如果不是第一到达数据,则直接写入文件        'F_Num(Pros_ID) = FreeFile()
            'Open App.Path & "/" & FileName For Binary Lock Write As #F_Num(Pros_ID)
            'If LOF(F_Num(Pros_ID)) > 0 Then Seek #F_Num(Pros_ID), LOF(F_Num(Pros_ID)) + 1
            Put #F_Num(Pros_ID), , DataByte()
           If Received_Byte(Pros_ID) >= File_Lenth(Pros_ID) Then Close #F_Num(Pros_ID) '写完关闭
    限制每次循环就只有一句,就是Put #F_Num(Pros_ID), , DataByte(),应该就是这句占cpu最厉害了。是不是应该等到全部接受完毕再写?不应该每次接收到一点就写文件?那样是不是可以提高效率?
      

  9.   

    有些看不懂:
    若是用winsock数组,那么dataArrival事件中应该有Index参数,为什么要
    If SaveData(DataByte(), "wizenflower.gif", 0) = False Then '调用一个winsock(0)
               ...
            End If        If SaveData(DataByte(), "wizenflower.gif", 1) = False Then '再调用一个winsock(1)
               ...
            End If
    这样重复调用来写呢?
    If SaveData(DataByte(), "wizenflower.gif", Index) = False Then
    一次不就行了吗?
    文件只需Open一次的问题,前面有人提过了,在这种结构下,不知你怎么改的?我觉得很乱,因为对你用多winsock目的,看不清楚,是为了分别下载多个文件,还是对同一文件多连接分段下载?还有多winsock,并不是多线程,因为再多winsock都是在同一线程中的。要想实现多线程下载可通过ActiveExe实现。最后,还想说说,这样处理Http协议,的确是对VB的一种侮辱!再怎么说也不能一个一个字节的找头部信息呀!头部Tag,应该在Split后用select结构处理。
    下面是我05年写的一个超简单示例
    http://blog.csdn.net/homezj/archive/2005/04/12/344861.aspx
    当时是为回应一个提问,写得仓促,现在看来,待完善的还有不少,但对你这个代码,还是有参考价值的。
      

  10.   

    恩~建议你先看看小小吉的代码
    但是我觉得你接收到一点写入一点不会有什么大问题的应该
    我写即时通讯文件传送都是接收一点就写入一点,也不见得cpu占用特别高啊,何况我那数据量也不是一般的大那种你可以先试试把数据都写入byte数组最后一并写入,但是我觉得这样做不太现实,万一你文件一大怎么办?
      

  11.   

    多谢指点,我没有用于多线程,也不是分段下载,只是用来下载一个文件的。所以简单的看成一个文件接受后,如何处理才能减轻cpu占用率,限制是接收到数据,就写数据,我猜是不是这个原因。多谢homezj对于协议的指点io是瓶颈?或者硬盘太烂了,很耗cpu资源
      

  12.   

    若只是一个文件下载,我上面提到的代码是可以解决的。
    理论上VB不用数组,收到数据直接put进文件,系统是会自动使用磁盘缓存的,但若不断open再close就不会了,因为一旦Close,系统必然要清缓存并写盘,这肯定会因为等待IO响应,而占用CPU。
    你前面CPU占用高的原因,可能源于频繁open与Close,改过后,还存在,不太理解,可能是其它代码引起的,也可能是对其的修改不正确?因为从现在代码中看不出其它引起Cpu占用的问题。你可在 
    If Arrival_First_Times(Pros_ID) = False
    这句后,加个Debug.Print "First",看看这段在下载中被执行多少次,因为从盘面上看,排除Open问题后,只有可能是这段反复执行,才会引起高CPU占用,它的效率实在太低了!
      

  13.   

    多谢各位指点,这两日有点事情,刚回来,问题尚未解决,但是非常感谢。cpu占用依然60-80,苦恼。
      

  14.   

    倒,你看看哪些地方还在不断 open close,我怀疑你别的地方有问题
    光就文件传输来说,出现这种情况很少见