我的输入是位于 Amazon S3 服务器上的一长串文件。我想下载文件的元数据,计算本地文件的哈希值,并将元数据哈希值与本地文件的哈希值进行比较。
目前,我使用一个循环来异步启动所有元数据下载,然后在每次完成后,根据需要在本地文件上计算 MD5 并进行比较。这是代码(只是相关行):
Dim s3client As New AmazonS3Client(KeyId.Text, keySecret.Text)
Dim responseTasks As New List(Of System.Tuple(Of ListViewItem, Task(Of GetObjectMetadataResponse)))
For Each lvi As ListViewItem In lvStatus.Items
Dim gomr As New Amazon.S3.Model.GetObjectMetadataRequest
gomr.BucketName = S3FileDialog.GetBucketName(lvi.SubItems(2).Text)
gomr.Key = S3FileDialog.GetPrefix(lvi.SubItems(2).Text)
responseTasks.Add(New System.Tuple(Of ListViewItem, Task(Of GetObjectMetadataResponse))(lvi, s3client.GetObjectMetadataAsync(gomr)))
Next
For Each t As System.Tuple(Of ListViewItem, Task(Of GetObjectMetadataResponse)) In responseTasks
Dim response As GetObjectMetadataResponse = Await t.Item2
If response.ETag.Trim(""""c) = MD5CalcFile(lvi.SubItems(1).Text) Then
lvi.SubItems(3).Text = "Match"
UpdateLvi(lvi)
End If
Next
我有两个问题:
我正在按照我制作它们的顺序等待回复。我宁愿按照它们完成的顺序处理它们,以便我更快地得到它们。
MD5 计算时间长且同步。我尝试使其异步但进程锁定。我认为 MD5 任务已添加到 .Net 任务列表的末尾,并且直到所有下载完成后才开始运行。
理想情况下,我在响应到达时对其进行处理,而不是按顺序处理,MD5 是异步的,但有机会运行。
编辑:
结合WhenAll,它现在看起来像这样:
Dim s3client As New Amazon.S3.AmazonS3Client(KeyId.Text, keySecret.Text)
Dim responseTasks As New Dictionary(Of Task(Of GetObjectMetadataResponse), ListViewItem)
For Each lvi As ListViewItem In lvStatus.Items
Dim gomr As New Amazon.S3.Model.GetObjectMetadataRequest
gomr.BucketName = S3FileDialog.GetBucketName(lvi.SubItems(2).Text)
gomr.Key = S3FileDialog.GetPrefix(lvi.SubItems(2).Text)
responseTasks.Add(s3client.GetObjectMetadataAsync(gomr), lvi)
Next
Dim startTime As DateTimeOffset = DateTimeOffset.Now
Do While responseTasks.Count > 0
Dim currentTask As Task(Of GetObjectMetadataResponse) = Await Task.WhenAny(responseTasks.Keys)
Dim response As GetObjectMetadataResponse = Await currentTask
If response.ETag.Trim(""""c) = MD5CalcFile(lvi.SubItems(1).Text) Then
lvi.SubItems(3).Text = "Match"
UpdateLvi(lvi)
End If
Loop
MsgBox((DateTimeOffset.Now - startTime).ToString)
只要 MDSCalcFile 完成,UI 就会立即锁定。整个循环大约需要 45 秒,第一个文件的 MD5 结果发生在开始后的 1 秒内。
如果我将行更改为:
If response.ETag.Trim(""""c) = Await Task.Run(Function () MD5CalcFile(lvi.SubItems(1).Text)) Then
完成 MD5CalcFile 后,UI 不会锁定。整个循环大约需要 75 秒,从 45 秒开始,第一个文件的 MD5 结果发生在等待 40 秒之后。
编辑2:
我找到了一个适合我的解决方案。问题出在我的 GetObjectMetadataAsync 中。我写错了。评论中错误的正确版本如下:
<System.Runtime.CompilerServices.Extension>
Function GetObjectMetadataAsync(a As AmazonS3Client, l As GetObjectMetadataRequest) As Task(Of GetObjectMetadataResponse)
Return Task.Factory.FromAsync(AddressOf a.BeginGetObjectMetadata, AddressOf a.EndGetObjectMetadata, l, Nothing)
'Return Task.Run(Function()
' Try
' Return a.GetObjectMetadata(l)
' Catch ex As Amazon.S3.AmazonS3Exception
' If ex.ErrorCode = "NoSuchKey" Then
' Return Nothing
' Else
' Throw ex
' End If
' End Try
' End Function)
End Function
我不知道为什么将同步版本放入线程或使用 FromAsync 很重要,但显然后者更好看,测试表明它要快得多。