私の入力は、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
2 つの問題があります。
頂いた順に回答お待ちしております。より速く取得できるように、完了した順に処理したいと思います。
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 がロックアップしません。ループ全体は 45 秒から約 75 秒かかり、最初のファイルの 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 を使用するかが問題になる理由はわかりませんが、明らかに後者の方が見栄えがよく、テストでははるかに高速であることが示されています。