为什么这段代码非常耗费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
请帮忙看看,为什么用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
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数组那么大,还一个个的循环查找不慢死你才怪
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%,在下载过程中持续如此。真是奇怪
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
后面就是图片数据了,所以循环最多几百次啊。而且仅在第一次数据到达时才循环查找数据开始的标志,就是两个回车,以后就不查询了。
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
你不要每次数据到达都open文件
open这个动作是非常慢的,因为每次都要读写磁盘,cpu都要等待磁盘的回应解决的办法是,你一开始open一次,然后文件不要关闭,直到收完才close,你试试看
'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最厉害了。是不是应该等到全部接受完毕再写?不应该每次接收到一点就写文件?那样是不是可以提高效率?
若是用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
当时是为回应一个提问,写得仓促,现在看来,待完善的还有不少,但对你这个代码,还是有参考价值的。
但是我觉得你接收到一点写入一点不会有什么大问题的应该
我写即时通讯文件传送都是接收一点就写入一点,也不见得cpu占用特别高啊,何况我那数据量也不是一般的大那种你可以先试试把数据都写入byte数组最后一并写入,但是我觉得这样做不太现实,万一你文件一大怎么办?
理论上VB不用数组,收到数据直接put进文件,系统是会自动使用磁盘缓存的,但若不断open再close就不会了,因为一旦Close,系统必然要清缓存并写盘,这肯定会因为等待IO响应,而占用CPU。
你前面CPU占用高的原因,可能源于频繁open与Close,改过后,还存在,不太理解,可能是其它代码引起的,也可能是对其的修改不正确?因为从现在代码中看不出其它引起Cpu占用的问题。你可在
If Arrival_First_Times(Pros_ID) = False
这句后,加个Debug.Print "First",看看这段在下载中被执行多少次,因为从盘面上看,排除Open问题后,只有可能是这段反复执行,才会引起高CPU占用,它的效率实在太低了!
光就文件传输来说,出现这种情况很少见